[Infra] Use asset details flyout overview and metadata for containers (#183767)

Closes #183727
## Summary

This PR adds asset details flyout to the inventory containers view. The
feature flag `observability:enableInfrastructureContainerAssetView`
should be enabled to see it.
To handle the asset type switching in the asset details flyout this PR
adds asset type to the `assetDetailsFlyout` URL param

Before:

![image](3b4fe8a1-d874-46a4-801f-2e0f91880034)

After: 
- Overview

![image](df54cc5c-f80f-40c8-9189-9d390e2c4eff)
- Metadata

![image](cd4a5ba9-33fc-428a-bb0f-571d820260fc)

## Testing 
- Enable `observability:enableInfrastructureContainerAssetView` in infra
settings
- Go to containers view, click on a container, and check the content


5ba578e6-6cdd-4561-a1f6-27ff4962ef6f
This commit is contained in:
jennypavlova 2024-05-21 11:17:06 +02:00 committed by GitHub
parent 742dff0f22
commit df74eb609c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 81 additions and 29 deletions

View file

@ -90,6 +90,13 @@ const dashboardsTab: Tab = {
),
};
const linkToApmTab: Tab = {
id: ContentTabIds.LINK_TO_APM,
name: i18n.translate('xpack.infra.assetDetails.tabs.linkToApm', {
defaultMessage: 'APM',
}),
};
export const hostDetailsTabs: Tab[] = [
overviewTab,
metadataTab,
@ -101,8 +108,11 @@ export const hostDetailsTabs: Tab[] = [
osqueryTab,
dashboardsTab,
];
export const hostDetailsFlyoutTabs: Tab[] = [...hostDetailsTabs, linkToApmTab];
// Profiling and Logs tab would be added in next iteration
export const containerDetailsTabs: Tab[] = [overviewTab, metadataTab];
export const containerDetailsFlyoutTabs: Tab[] = [overviewTab, metadataTab, linkToApmTab];
export const getAssetDetailsTabs = (type: string): Tab[] => {
switch (type) {
@ -114,3 +124,14 @@ export const getAssetDetailsTabs = (type: string): Tab[] => {
return [];
}
};
export const getAssetDetailsFlyoutTabs = (type: string): Tab[] => {
switch (type) {
case 'host':
return hostDetailsFlyoutTabs;
case 'container':
return containerDetailsFlyoutTabs;
default:
return [];
}
};

View file

@ -5,12 +5,18 @@
* 2.0.
*/
import { INTEGRATION_NAME } from './types';
import { INTEGRATION_NAME, ASSET_DETAILS_ASSET_TYPE } from './types';
export const ASSET_DETAILS_FLYOUT_COMPONENT_NAME = 'infraAssetDetailsFlyout';
export const ASSET_DETAILS_PAGE_COMPONENT_NAME = 'infraAssetDetailsPage';
export const APM_HOST_FILTER_FIELD = 'host.hostname';
export const APM_CONTAINER_FILTER_FIELD = 'container.id';
export const APM_FILTER_FIELD_PER_ASSET_TYPE = {
[ASSET_DETAILS_ASSET_TYPE.container]: APM_CONTAINER_FILTER_FIELD,
[ASSET_DETAILS_ASSET_TYPE.host]: APM_HOST_FILTER_FIELD,
};
export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails';

View file

@ -22,11 +22,11 @@ import { usePluginConfig } from '../../../containers/plugin_config_context';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { useProfilingIntegrationSetting } from '../../../hooks/use_profiling_integration_setting';
import { CreateAlertRuleButton } from '../../shared/alerts/links/create_alert_rule_button';
import { APM_HOST_FILTER_FIELD } from '../constants';
import { LinkToNodeDetails } from '../links';
import { ContentTabIds, type LinkOptions, type RouteState, type Tab, type TabIds } from '../types';
import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props';
import { useTabSwitcherContext } from './use_tab_switcher';
import { getApmField } from '../utils';
type TabItem = NonNullable<Pick<EuiPageHeaderProps, 'tabs'>['tabs']>[number];
@ -157,7 +157,7 @@ const useTabs = (tabs: Tab[]) => {
app: 'apm',
hash: 'traces',
search: {
kuery: `${APM_HOST_FILTER_FIELD}:"${asset.name}"`,
kuery: `${getApmField(asset.type)}:"${asset.id}"`,
},
});

View file

@ -102,3 +102,8 @@ export enum INTEGRATION_NAME {
kubernetesContainer = 'kubernetesContainer',
docker = 'docker',
}
export enum ASSET_DETAILS_ASSET_TYPE {
container = 'container',
host = 'host',
}

View file

@ -5,8 +5,9 @@
* 2.0.
*/
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import type { InfraMetadata } from '../../../common/http_api';
import { INTEGRATIONS } from './constants';
import { INTEGRATIONS, APM_FILTER_FIELD_PER_ASSET_TYPE } from './constants';
export const toTimestampRange = ({ from, to }: { from: string; to: string }) => {
const fromTs = new Date(from).getTime();
@ -34,3 +35,14 @@ export const getIntegrationsAvailable = (metadata?: InfraMetadata | null) => {
.filter(([_, fields]) => metadata?.features?.some((f) => fields.includes(f.name)))
.map(([name]) => name);
};
export const getApmField = (assetType: InventoryItemType): string => {
switch (assetType) {
case 'host':
return APM_FILTER_FIELD_PER_ASSET_TYPE.host;
case 'container':
return APM_FILTER_FIELD_PER_ASSET_TYPE.container;
default:
return '';
}
};

View file

@ -7,7 +7,7 @@
import { i18n } from '@kbn/i18n';
import { usePerformanceContext } from '@kbn/ebt-tools';
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import { useCurrentEuiBreakpoint } from '@elastic/eui';
import { euiStyled } from '@kbn/kibana-react-plugin/common';
import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
@ -63,11 +63,20 @@ export const NodesOverview = ({
isAutoReloading,
}: Props) => {
const currentBreakpoint = useCurrentEuiBreakpoint();
const [{ detailsItemId }, setFlyoutUrlState] = useAssetDetailsFlyoutState();
const [{ detailsItemId, assetType }, setFlyoutUrlState] = useAssetDetailsFlyoutState();
const { onPageReady } = usePerformanceContext();
const nodeName = useMemo(
() => nodes.find((node) => node.path[0].value === detailsItemId)?.name,
[detailsItemId, nodes]
);
const closeFlyout = useCallback(
() => setFlyoutUrlState({ detailsItemId: null }),
() =>
setFlyoutUrlState({
detailsItemId: null,
assetType: null,
}),
[setFlyoutUrlState]
);
@ -149,9 +158,10 @@ export const NodesOverview = ({
bottomMargin={bottomMargin}
staticHeight={isStatic}
/>
{nodeType === 'host' && detailsItemId && (
{nodeType === assetType && detailsItemId && (
<AssetDetailsFlyout
assetName={detailsItemId}
assetId={detailsItemId}
assetName={nodeName}
assetType={nodeType}
closeFlyout={closeFlyout}
currentTime={currentTime}

View file

@ -5,17 +5,16 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import React, { useMemo } from 'react';
import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import type { InfraWaffleMapOptions } from '../../../../../lib/lib';
import { ContentTabIds } from '../../../../../components/asset_details/types';
import { AssetDetails } from '../../../../../components/asset_details';
import { useSourceContext } from '../../../../../containers/metrics_source';
import { hostDetailsTabs } from '../../../../../common/asset_details_config/asset_details_tabs';
import { getAssetDetailsFlyoutTabs } from '../../../../../common/asset_details_config/asset_details_tabs';
interface Props {
assetName: string;
assetName?: string;
assetId: string;
assetType: InventoryItemType;
closeFlyout: () => void;
currentTime: number;
@ -24,20 +23,11 @@ interface Props {
refreshInterval?: number;
}
const flyoutTabs = [
...hostDetailsTabs,
{
id: ContentTabIds.LINK_TO_APM,
name: i18n.translate('xpack.infra.assetDetails.tabs.linkToApm', {
defaultMessage: 'APM',
}),
},
];
const ONE_HOUR = 60 * 60 * 1000;
export const AssetDetailsFlyout = ({
assetName,
assetId,
assetType,
closeFlyout,
currentTime,
@ -62,7 +52,7 @@ export const AssetDetailsFlyout = ({
return source ? (
<AssetDetails
assetId={assetName}
assetId={assetId}
assetName={assetName}
assetType={assetType}
overrides={{
@ -73,7 +63,7 @@ export const AssetDetailsFlyout = ({
options,
},
}}
tabs={flyoutTabs}
tabs={getAssetDetailsFlyoutTabs(assetType)}
links={['nodeDetails']}
renderMode={{
mode: 'flyout',

View file

@ -11,6 +11,8 @@ import { first } from 'lodash';
import { EuiPopover, EuiToolTip } from '@elastic/eui';
import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import { useBoolean } from '@kbn/react-hooks';
import { useUiSetting } from '@kbn/kibana-react-plugin/public';
import { enableInfrastructureContainerAssetView } from '@kbn/observability-plugin/common';
import {
InfraWaffleMapBounds,
InfraWaffleMapNode,
@ -54,9 +56,13 @@ export const Node = ({
const color = colorFromValue(options.legend, rawValue, bounds);
const value = formatter(rawValue);
const isContainerAssetViewEnabled = useUiSetting(enableInfrastructureContainerAssetView);
const showContainerAssetDetailPage = nodeType === 'container' && isContainerAssetViewEnabled;
const toggleAssetPopover = () => {
if (nodeType === 'host') {
setFlyoutUrlState({ detailsItemId: node.name });
if (nodeType === 'host' || showContainerAssetDetailPage) {
setFlyoutUrlState({ detailsItemId: node.id, assetType: nodeType });
} else {
togglePopover();
}
@ -71,7 +77,7 @@ export const Node = ({
color={color}
nodeName={node.name}
value={value}
showBorder={detailsItemId === node.name || isPopoverOpen}
showBorder={detailsItemId === node.id || isPopoverOpen}
/>
);

View file

@ -13,6 +13,7 @@ import { useUrlState } from '../../../../utils/use_url_state';
export const GET_DEFAULT_PROPERTIES: AssetDetailsFlyoutProperties = {
detailsItemId: null,
assetType: null,
};
const ASSET_DETAILS_FLYOUT_URL_STATE_KEY = 'assetDetailsFlyout';
@ -35,6 +36,7 @@ export const useAssetDetailsFlyoutState = (): [
const AssetDetailsFlyoutStateRT = rt.type({
detailsItemId: rt.union([rt.string, rt.null]),
assetType: rt.union([rt.string, rt.null]),
});
export type AssetDetailsFlyoutState = rt.TypeOf<typeof AssetDetailsFlyoutStateRT>;