mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Fix metrics to uptime redirection with locator (#125098)
* move locator registration to plugin setup * make locator naming consistent * use locator in inventory view * update locator to handle supported host types * try another import * remove locator constant * Revert "remove locator constant" This reverts commit84416b00ca
. * Revert "try another import" This reverts commitb42ac97b40
. * add share plugin type * reduce constant import scope * fix tests * use uptime locator in waffle context menu * remove obsolete create_uptime_link files * use host.ip instead of monitor.ip * align locator to infra implementation * navigate_to_uptime helper * use navigate_to_uptime helper * fix waffle link color * lint Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
47c861bbad
commit
6ea7541ada
12 changed files with 81 additions and 170 deletions
|
@ -3,6 +3,7 @@
|
|||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"requiredPlugins": [
|
||||
"share",
|
||||
"features",
|
||||
"usageCollection",
|
||||
"spaces",
|
||||
|
|
|
@ -25,7 +25,8 @@ import { OVERLAY_Y_START, OVERLAY_BOTTOM_MARGIN } from './tabs/shared';
|
|||
import { useLinkProps } from '../../../../../../../observability/public';
|
||||
import { getNodeDetailUrl } from '../../../../link_to';
|
||||
import { findInventoryModel } from '../../../../../../common/inventory_models';
|
||||
import { createUptimeLink } from '../../lib/create_uptime_link';
|
||||
import { navigateToUptime } from '../../lib/navigate_to_uptime';
|
||||
import { InfraClientCoreStart, InfraClientStartDeps } from '../../../../../types';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
|
@ -49,7 +50,8 @@ export const NodeContextPopover = ({
|
|||
const tabConfigs = [MetricsTab, LogsTab, ProcessesTab, PropertiesTab, AnomaliesTab, OsqueryTab];
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const { application, share } = useKibana<InfraClientCoreStart & InfraClientStartDeps>().services;
|
||||
const uiCapabilities = application?.capabilities;
|
||||
const canCreateAlerts = useMemo(
|
||||
() => Boolean(uiCapabilities?.infrastructure?.save),
|
||||
[uiCapabilities]
|
||||
|
@ -91,7 +93,6 @@ export const NodeContextPopover = ({
|
|||
kuery: `${apmField}:"${node.id}"`,
|
||||
},
|
||||
});
|
||||
const uptimeMenuItemLinkProps = useLinkProps(createUptimeLink(options, nodeType, node));
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
|
@ -164,7 +165,7 @@ export const NodeContextPopover = ({
|
|||
defaultMessage="APM"
|
||||
/>
|
||||
</EuiTab>
|
||||
<EuiTab {...uptimeMenuItemLinkProps}>
|
||||
<EuiTab onClick={() => navigateToUptime(share.url.locators, nodeType, node)}>
|
||||
<EuiIcon type="popout" />{' '}
|
||||
<FormattedMessage
|
||||
id="xpack.infra.infra.nodeDetails.updtimeTabLabel"
|
||||
|
|
|
@ -13,7 +13,6 @@ import React, { useMemo, useState } from 'react';
|
|||
import { AlertFlyout } from '../../../../../alerting/inventory/components/alert_flyout';
|
||||
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib';
|
||||
import { getNodeDetailUrl, getNodeLogsUrl } from '../../../../link_to';
|
||||
import { createUptimeLink } from '../../lib/create_uptime_link';
|
||||
import { findInventoryModel, findInventoryFields } from '../../../../../../common/inventory_models';
|
||||
import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { InventoryItemType } from '../../../../../../common/inventory_models/types';
|
||||
|
@ -28,6 +27,8 @@ import {
|
|||
ActionMenuDivider,
|
||||
} from '../../../../../../../observability/public';
|
||||
import { useLinkProps } from '../../../../../../../observability/public';
|
||||
import { navigateToUptime } from '../../lib/navigate_to_uptime';
|
||||
import { InfraClientCoreStart, InfraClientStartDeps } from '../../../../../types';
|
||||
|
||||
interface Props {
|
||||
options: InfraWaffleMapOptions;
|
||||
|
@ -41,7 +42,9 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
const [flyoutVisible, setFlyoutVisible] = useState(false);
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
|
||||
const uiCapabilities = useKibana().services.application?.capabilities;
|
||||
const { application, share } = useKibana<InfraClientCoreStart & InfraClientStartDeps>()
|
||||
.services;
|
||||
const uiCapabilities = application?.capabilities;
|
||||
// Due to the changing nature of the fields between APM and this UI,
|
||||
// We need to have some exceptions until 7.0 & ECS is finalized. Reference
|
||||
// #26620 for the details for these fields.
|
||||
|
@ -95,7 +98,6 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
kuery: `${apmField}:"${node.id}"`,
|
||||
},
|
||||
});
|
||||
const uptimeMenuItemLinkProps = useLinkProps(createUptimeLink(options, nodeType, node));
|
||||
|
||||
const nodeLogsMenuItem: SectionLinkProps = {
|
||||
label: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
|
||||
|
@ -131,7 +133,7 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
defaultMessage: '{inventoryName} in Uptime',
|
||||
values: { inventoryName: inventoryModel.singularDisplayName },
|
||||
}),
|
||||
...uptimeMenuItemLinkProps,
|
||||
onClick: () => navigateToUptime(share.url.locators, nodeType, node),
|
||||
isDisabled: !showUptimeLink,
|
||||
};
|
||||
|
||||
|
@ -171,7 +173,7 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
<SectionLink data-test-subj="viewLogsContextMenuItem" {...nodeLogsMenuItem} />
|
||||
<SectionLink {...nodeDetailMenuItem} />
|
||||
<SectionLink data-test-subj="viewApmTracesContextMenuItem" {...apmTracesMenuItem} />
|
||||
<SectionLink {...uptimeMenuItem} />
|
||||
<SectionLink {...uptimeMenuItem} color={'primary'} />
|
||||
</SectionLinks>
|
||||
<ActionMenuDivider />
|
||||
<SectionLinks>
|
||||
|
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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 { createUptimeLink } from './create_uptime_link';
|
||||
import { InfraWaffleMapOptions, InfraFormatterType } from '../../../../lib/lib';
|
||||
import { SnapshotMetricType } from '../../../../../common/inventory_models/types';
|
||||
|
||||
const options: InfraWaffleMapOptions = {
|
||||
formatter: InfraFormatterType.percent,
|
||||
formatTemplate: '{{value}}',
|
||||
metric: { type: 'cpu' },
|
||||
groupBy: [],
|
||||
sort: { by: 'name', direction: 'asc' },
|
||||
legend: {
|
||||
type: 'gradient',
|
||||
rules: [],
|
||||
},
|
||||
};
|
||||
|
||||
describe('createUptimeLink()', () => {
|
||||
it('should work for hosts with ip', () => {
|
||||
const node = {
|
||||
pathId: 'host-01',
|
||||
id: 'host-01',
|
||||
name: 'host-01',
|
||||
ip: '10.0.1.2',
|
||||
path: [],
|
||||
metrics: [
|
||||
{
|
||||
name: 'cpu' as SnapshotMetricType,
|
||||
value: 0.5,
|
||||
max: 0.8,
|
||||
avg: 0.6,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(createUptimeLink(options, 'host', node)).toStrictEqual({
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: { search: 'host.ip:"10.0.1.2"' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for hosts without ip', () => {
|
||||
const node = {
|
||||
pathId: 'host-01',
|
||||
id: 'host-01',
|
||||
name: 'host-01',
|
||||
path: [],
|
||||
metrics: [
|
||||
{
|
||||
name: 'cpu' as SnapshotMetricType,
|
||||
value: 0.5,
|
||||
max: 0.8,
|
||||
avg: 0.6,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(createUptimeLink(options, 'host', node)).toStrictEqual({
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: { search: 'host.name:"host-01"' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for pods', () => {
|
||||
const node = {
|
||||
pathId: 'pod-01',
|
||||
id: '29193-pod-02939',
|
||||
name: 'pod-01',
|
||||
path: [],
|
||||
metrics: [
|
||||
{
|
||||
name: 'cpu' as SnapshotMetricType,
|
||||
value: 0.5,
|
||||
max: 0.8,
|
||||
avg: 0.6,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(createUptimeLink(options, 'pod', node)).toStrictEqual({
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: { search: 'kubernetes.pod.uid:"29193-pod-02939"' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should work for container', () => {
|
||||
const node = {
|
||||
pathId: 'docker-01',
|
||||
id: 'docker-1234',
|
||||
name: 'docker-01',
|
||||
path: [],
|
||||
metrics: [
|
||||
{
|
||||
name: 'cpu' as SnapshotMetricType,
|
||||
value: 0.5,
|
||||
max: 0.8,
|
||||
avg: 0.6,
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(createUptimeLink(options, 'container', node)).toStrictEqual({
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: { search: 'container.id:"docker-1234"' },
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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 { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../lib/lib';
|
||||
import { InventoryItemType } from '../../../../../common/inventory_models/types';
|
||||
import { getFieldByType } from '../../../../../common/inventory_models';
|
||||
import { LinkDescriptor } from '../../../../../../observability/public';
|
||||
|
||||
export const createUptimeLink = (
|
||||
options: InfraWaffleMapOptions,
|
||||
nodeType: InventoryItemType,
|
||||
node: InfraWaffleMapNode
|
||||
): LinkDescriptor => {
|
||||
if (nodeType === 'host' && node.ip) {
|
||||
return {
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: {
|
||||
search: `host.ip:"${node.ip}"`,
|
||||
},
|
||||
};
|
||||
}
|
||||
const field = getFieldByType(nodeType);
|
||||
return {
|
||||
app: 'uptime',
|
||||
hash: '/',
|
||||
search: {
|
||||
search: `${field ? field + ':' : ''}"${node.id}"`,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 { InfraWaffleMapNode } from '../../../../lib/lib';
|
||||
import { InventoryItemType } from '../../../../../common/inventory_models/types';
|
||||
import { uptimeOverviewLocatorID } from '../../../../../../observability/public';
|
||||
import { LocatorClient } from '../../../../../../../../src/plugins/share/common/url_service/locators';
|
||||
|
||||
export const navigateToUptime = (
|
||||
locators: LocatorClient,
|
||||
nodeType: InventoryItemType,
|
||||
node: InfraWaffleMapNode
|
||||
) => {
|
||||
return locators.get(uptimeOverviewLocatorID)!.navigate({ [nodeType]: node.id, ip: node.ip });
|
||||
};
|
|
@ -10,6 +10,7 @@ import { IHttpFetchError } from 'src/core/public';
|
|||
import type { DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
import type { EmbeddableSetup, EmbeddableStart } from '../../../../src/plugins/embeddable/public';
|
||||
import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import type { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
|
||||
import type {
|
||||
UsageCollectionSetup,
|
||||
UsageCollectionStart,
|
||||
|
@ -54,6 +55,7 @@ export interface InfraClientSetupDeps {
|
|||
usageCollection: UsageCollectionSetup;
|
||||
ml: MlPluginSetup;
|
||||
embeddable: EmbeddableSetup;
|
||||
share: SharePluginSetup;
|
||||
}
|
||||
|
||||
export interface InfraClientStartDeps {
|
||||
|
@ -66,6 +68,7 @@ export interface InfraClientStartDeps {
|
|||
ml: MlPluginStart;
|
||||
embeddable?: EmbeddableStart;
|
||||
osquery?: unknown; // OsqueryPluginStart;
|
||||
share: SharePluginStart;
|
||||
}
|
||||
|
||||
export type InfraClientCoreSetup = CoreSetup<InfraClientStartDeps, InfraClientStartExports>;
|
||||
|
|
|
@ -33,4 +33,4 @@ export const casesPath = '/cases';
|
|||
|
||||
// Name of a locator created by the uptime plugin. Intended for use
|
||||
// by other plugins as well, so defined here to prevent cross-references.
|
||||
export const uptimeOverviewLocatorID = 'uptime-overview-locator';
|
||||
export const uptimeOverviewLocatorID = 'UPTIME_OVERVIEW_LOCATOR';
|
||||
|
|
|
@ -25,17 +25,34 @@ describe('uptimeOverviewNavigatorParams', () => {
|
|||
});
|
||||
|
||||
it('creates a path with expected search when hostname is specified', async () => {
|
||||
const location = await uptimeOverviewNavigatorParams.getLocation({ hostname: 'elastic.co' });
|
||||
expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=url.domain: "elastic.co"`);
|
||||
const location = await uptimeOverviewNavigatorParams.getLocation({ host: 'elastic.co' });
|
||||
expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=host.name: "elastic.co"`);
|
||||
});
|
||||
|
||||
it('creates a path with expected search when multiple keys are specified', async () => {
|
||||
it('creates a path with expected search when multiple host keys are specified', async () => {
|
||||
const location = await uptimeOverviewNavigatorParams.getLocation({
|
||||
hostname: 'elastic.co',
|
||||
host: 'elastic.co',
|
||||
ip: '127.0.0.1',
|
||||
});
|
||||
expect(location.path).toEqual(
|
||||
`${OVERVIEW_ROUTE}?search=monitor.ip: "127.0.0.1" OR url.domain: "elastic.co"`
|
||||
`${OVERVIEW_ROUTE}?search=host.name: "elastic.co" OR host.ip: "127.0.0.1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a path with expected search when multiple kubernetes pod is specified', async () => {
|
||||
const location = await uptimeOverviewNavigatorParams.getLocation({
|
||||
pod: 'foo',
|
||||
ip: '10.0.0.1',
|
||||
});
|
||||
expect(location.path).toEqual(
|
||||
`${OVERVIEW_ROUTE}?search=kubernetes.pod.uid: "foo" OR monitor.ip: "10.0.0.1"`
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a path with expected search when docker container is specified', async () => {
|
||||
const location = await uptimeOverviewNavigatorParams.getLocation({
|
||||
container: 'foo',
|
||||
});
|
||||
expect(location.path).toEqual(`${OVERVIEW_ROUTE}?search=container.id: "foo"`);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,15 +6,31 @@
|
|||
*/
|
||||
|
||||
import { uptimeOverviewLocatorID } from '../../../../observability/public';
|
||||
import { OVERVIEW_ROUTE } from '../../../common/constants';
|
||||
import { OVERVIEW_ROUTE } from '../../../common/constants/ui';
|
||||
|
||||
const formatSearchKey = (key: string, value: string) => `${key}: "${value}"`;
|
||||
|
||||
async function navigate({ ip, hostname }: { ip?: string; hostname?: string }) {
|
||||
async function navigate({
|
||||
ip,
|
||||
host,
|
||||
container,
|
||||
pod,
|
||||
}: {
|
||||
ip?: string;
|
||||
host?: string;
|
||||
container?: string;
|
||||
pod?: string;
|
||||
}) {
|
||||
const searchParams: string[] = [];
|
||||
|
||||
if (ip) searchParams.push(formatSearchKey('monitor.ip', ip));
|
||||
if (hostname) searchParams.push(formatSearchKey('url.domain', hostname));
|
||||
if (host) searchParams.push(formatSearchKey('host.name', host));
|
||||
if (container) searchParams.push(formatSearchKey('container.id', container));
|
||||
if (pod) searchParams.push(formatSearchKey('kubernetes.pod.uid', pod));
|
||||
|
||||
if (ip) {
|
||||
const root = host ? 'host' : 'monitor';
|
||||
searchParams.push(formatSearchKey(`${root}.ip`, ip));
|
||||
}
|
||||
|
||||
const searchString = searchParams.join(' OR ');
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import {
|
|||
import { LazySyntheticsCustomAssetsExtension } from '../components/fleet_package/lazy_synthetics_custom_assets_extension';
|
||||
import { Start as InspectorPluginStart } from '../../../../../src/plugins/inspector/public';
|
||||
import { CasesUiStart } from '../../../cases/public';
|
||||
import { uptimeOverviewNavigatorParams } from './locators/overview';
|
||||
|
||||
export interface ClientPluginsSetup {
|
||||
home?: HomePublicPluginSetup;
|
||||
|
@ -104,6 +105,8 @@ export class UptimePlugin
|
|||
return UptimeDataHelper(coreStart);
|
||||
};
|
||||
|
||||
plugins.share.url.locators.create(uptimeOverviewNavigatorParams);
|
||||
|
||||
plugins.observability.dashboard.register({
|
||||
appName: 'synthetics',
|
||||
hasData: async () => {
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
} from '../../common/constants';
|
||||
import { UptimeApp, UptimeAppProps } from './uptime_app';
|
||||
import { ClientPluginsSetup, ClientPluginsStart } from './plugin';
|
||||
import { uptimeOverviewNavigatorParams } from './locators/overview';
|
||||
|
||||
export function renderApp(
|
||||
core: CoreStart,
|
||||
|
@ -41,8 +40,6 @@ export function renderApp(
|
|||
|
||||
const canSave = (capabilities.uptime.save ?? false) as boolean;
|
||||
|
||||
plugins.share.url.locators.create(uptimeOverviewNavigatorParams);
|
||||
|
||||
const props: UptimeAppProps = {
|
||||
isDev,
|
||||
plugins,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue