[8.3] [Session View]Metadata Tab (#131465)

* Metadata tab + new fields, edited Host tab jest, need to add more unit test and delete unused fields

* added accordion for Host, modified new fields

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* added more unit test for helper function, removed dropped fields from ECS spreadsheet, PR comments

* PR Comments, Fixing Checks issue, more Jest

* eslint

* changed the logic for checking which accordion not to render

* PR comments

* PR Comments, localized new accordion

* renaming file and component name from Host to Metadata

* merge main + PR comments

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Rickyanto Ang 2022-05-12 09:23:01 -07:00 committed by GitHub
parent cddd41dfae
commit e51e4579c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1023 additions and 430 deletions

View file

@ -149,6 +149,8 @@ export interface ProcessEvent {
kibana?: {
alert?: ProcessEventAlert;
};
container?: ProcessEventContainer;
orchestrator?: ProcessEventOrchestrator;
}
export interface ProcessEventsPage {
@ -187,3 +189,31 @@ export interface Process {
export type ProcessMap = {
[key: string]: Process;
};
export interface ProcessEventContainer {
id?: string;
name?: string;
image?: {
name?: string;
tag?: string;
hash?: {
all?: string;
};
};
}
export interface ProcessEventOrchestrator {
resource?: {
name?: string;
type?: string;
ip?: string;
};
namespace?: string;
cluster?: {
name?: string;
id?: string;
};
parent?: {
type?: string;
};
}

View file

@ -19,6 +19,8 @@ interface DetailPanelAccordionDeps {
tooltipContent?: string;
extraActionTitle?: string;
onExtraActionClick?: () => void;
children?: ReactNode;
initialIsOpen?: boolean;
}
/**
@ -31,6 +33,8 @@ export const DetailPanelAccordion = ({
tooltipContent,
extraActionTitle,
onExtraActionClick,
children,
initialIsOpen = false,
}: DetailPanelAccordionDeps) => {
const styles = useStyles();
@ -38,6 +42,7 @@ export const DetailPanelAccordion = ({
<EuiAccordion
id={id}
arrowDisplay="right"
initialIsOpen={initialIsOpen}
buttonContent={
<EuiFlexGroup
alignItems="center"
@ -71,6 +76,7 @@ export const DetailPanelAccordion = ({
data-test-subj="sessionView:detail-panel-accordion"
>
<DetailPanelDescriptionList listItems={listItems} />
{children}
</EuiAccordion>
);
};

View file

@ -25,6 +25,10 @@ export const useStyles = () => {
dl: {
paddingTop: '0px',
},
'&:only-child': {
border: euiTheme.border.thin,
borderRadius: euiTheme.border.radius.medium,
},
};
const accordionButton: CSSObject = {

View file

@ -1,85 +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 { ProcessEventHost } from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { getHostData } from './helpers';
const MOCK_HOST_DATA: ProcessEventHost = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
full: 'CentOS 7.9.2009',
kernel: '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021',
name: 'Linux',
platform: 'centos',
version: '7.9.2009',
},
};
describe('detail panel host tab helpers tests', () => {
it('getHostData returns fields with a dash with undefined host', () => {
const result = getHostData(undefined);
expect(result.architecture).toEqual(DASH);
expect(result.hostname).toEqual(DASH);
expect(result.id).toEqual(DASH);
expect(result.ip).toEqual(DASH);
expect(result.mac).toEqual(DASH);
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(DASH);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(DASH);
expect(result.os.name).toEqual(DASH);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(DASH);
});
it('getHostData returns dashes for missing fields', () => {
const result = getHostData({
...MOCK_HOST_DATA,
ip: ['127.0.0.1', '', '', 'fe80::7d39:3147:4d9a:f809'],
name: undefined,
os: {
...MOCK_HOST_DATA.os,
full: undefined,
platform: undefined,
},
});
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(['127.0.0.1', DASH, DASH, 'fe80::7d39:3147:4d9a:f809'].join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
it('getHostData returns all data provided', () => {
const result = getHostData(MOCK_HOST_DATA);
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(MOCK_HOST_DATA.ip?.join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(MOCK_HOST_DATA.name);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(MOCK_HOST_DATA.os?.full);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(MOCK_HOST_DATA.os?.platform);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
});

View file

@ -1,49 +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 { ProcessEventHost } from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { DetailPanelHost } from '../../types';
import { dataOrDash } from '../../utils/data_or_dash';
export const getHostData = (host: ProcessEventHost | undefined): DetailPanelHost => {
const detailPanelHost: DetailPanelHost = {
architecture: DASH,
hostname: DASH,
id: DASH,
ip: DASH,
mac: DASH,
name: DASH,
os: {
family: DASH,
full: DASH,
kernel: DASH,
name: DASH,
platform: DASH,
version: DASH,
},
};
if (!host) {
return detailPanelHost;
}
detailPanelHost.hostname = dataOrDash(host.hostname).toString();
detailPanelHost.id = dataOrDash(host.id).toString();
detailPanelHost.ip = host.ip?.map?.((ip) => dataOrDash(ip)).join(', ') ?? DASH;
detailPanelHost.mac = host.mac?.map?.((mac) => dataOrDash(mac)).join(', ') ?? DASH;
detailPanelHost.name = dataOrDash(host.name).toString();
detailPanelHost.architecture = dataOrDash(host.architecture).toString();
detailPanelHost.os.family = dataOrDash(host.os?.family).toString();
detailPanelHost.os.full = dataOrDash(host.os?.full).toString();
detailPanelHost.os.kernel = dataOrDash(host.os?.kernel).toString();
detailPanelHost.os.name = dataOrDash(host.os?.name).toString();
detailPanelHost.os.platform = dataOrDash(host.os?.platform).toString();
detailPanelHost.os.version = dataOrDash(host.os?.version).toString();
return detailPanelHost;
};

View file

@ -1,88 +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 React from 'react';
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
import { ProcessEventHost } from '../../../common/types/process_tree';
import { DetailPanelHostTab } from '.';
const TEST_ARCHITECTURE = 'x86_64';
const TEST_HOSTNAME = 'host-james-fleet-714-2';
const TEST_ID = '48c1b3f1ac5da4e0057fc9f60f4d1d5d';
const TEST_IP = ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'];
const TEST_MAC = ['42:01:0a:84:00:32'];
const TEST_NAME = 'name-james-fleet-714-2';
const TEST_OS_FAMILY = 'family-centos';
const TEST_OS_FULL = 'full-CentOS 7.9.2009';
const TEST_OS_KERNEL = '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021';
const TEST_OS_NAME = 'os-Linux';
const TEST_OS_PLATFORM = 'platform-centos';
const TEST_OS_VERSION = 'version-7.9.2009';
const TEST_HOST: ProcessEventHost = {
architecture: TEST_ARCHITECTURE,
hostname: TEST_HOSTNAME,
id: TEST_ID,
ip: TEST_IP,
mac: TEST_MAC,
name: TEST_NAME,
os: {
family: TEST_OS_FAMILY,
full: TEST_OS_FULL,
kernel: TEST_OS_KERNEL,
name: TEST_OS_NAME,
platform: TEST_OS_PLATFORM,
version: TEST_OS_VERSION,
},
};
describe('DetailPanelHostTab component', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let mockedContext: AppContextTestRender;
beforeEach(() => {
mockedContext = createAppRootMockRenderer();
});
describe('When DetailPanelHostTab is mounted', () => {
it('renders DetailPanelHostTab correctly', async () => {
renderResult = mockedContext.render(<DetailPanelHostTab processHost={TEST_HOST} />);
expect(renderResult.queryByText('architecture')).toBeVisible();
expect(renderResult.queryByText('hostname')).toBeVisible();
expect(renderResult.queryByText('id')).toBeVisible();
expect(renderResult.queryByText('ip')).toBeVisible();
expect(renderResult.queryByText('mac')).toBeVisible();
expect(renderResult.queryByText('name')).toBeVisible();
expect(renderResult.queryByText(TEST_ARCHITECTURE)).toBeVisible();
expect(renderResult.queryByText(TEST_HOSTNAME)).toBeVisible();
expect(renderResult.queryByText(TEST_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_IP.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_MAC.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_NAME)).toBeVisible();
// expand host os accordion
renderResult
.queryByTestId('sessionView:detail-panel-accordion')
?.querySelector('button')
?.click();
expect(renderResult.queryByText('os.family')).toBeVisible();
expect(renderResult.queryByText('os.full')).toBeVisible();
expect(renderResult.queryByText('os.kernel')).toBeVisible();
expect(renderResult.queryByText('os.name')).toBeVisible();
expect(renderResult.queryByText('os.platform')).toBeVisible();
expect(renderResult.queryByText('os.version')).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FAMILY)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FULL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_KERNEL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_PLATFORM)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_VERSION)).toBeVisible();
});
});
});

View file

@ -1,198 +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 React, { useMemo } from 'react';
import { EuiTextColor } from '@elastic/eui';
import { ProcessEventHost } from '../../../common/types/process_tree';
import { DetailPanelAccordion } from '../detail_panel_accordion';
import { DetailPanelCopy } from '../detail_panel_copy';
import { DetailPanelDescriptionList } from '../detail_panel_description_list';
import { DetailPanelListItem } from '../detail_panel_list_item';
import { useStyles } from '../detail_panel_process_tab/styles';
import { getHostData } from './helpers';
interface DetailPanelHostTabDeps {
processHost?: ProcessEventHost;
}
/**
* Host Panel of session view detail panel.
*/
export const DetailPanelHostTab = ({ processHost }: DetailPanelHostTabDeps) => {
const styles = useStyles();
const hostData = useMemo(() => getHostData(processHost), [processHost]);
return (
<>
<DetailPanelDescriptionList
listItems={[
{
title: <DetailPanelListItem>hostname</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.hostname: "${hostData.hostname}"`}
tooltipContent={hostData.hostname}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.hostname}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.id: "${hostData.id}"`}
tooltipContent={hostData.id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.id}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>ip</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.ip: "${hostData.ip}"`}
tooltipContent={hostData.ip}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.ip}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>mac</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.mac: "${hostData.mac}"`}
tooltipContent={hostData.mac}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.mac}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.name: "${hostData.name}"`}
tooltipContent={hostData.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
/>
<DetailPanelAccordion
id="hostOS"
title="Host OS"
listItems={[
{
title: <DetailPanelListItem>architecture</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.architecture: "${hostData.architecture}"`}
tooltipContent={hostData.architecture}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.architecture}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.family</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.family: "${hostData.os.family}"`}
tooltipContent={hostData.os.family}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.family}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.full</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.full: "${hostData.os.full}"`}
tooltipContent={hostData.os.full}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.full}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.kernel</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.kernel: "${hostData.os.kernel}"`}
tooltipContent={hostData.os.kernel}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.kernel}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.name: "${hostData.os.name}"`}
tooltipContent={hostData.os.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.platform</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.platform: "${hostData.os.platform}"`}
tooltipContent={hostData.os.platform}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.platform}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.version</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.version: "${hostData.os.version}"`}
tooltipContent={hostData.os.version}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.version}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
/>
</>
);
};

View file

@ -0,0 +1,181 @@
/*
* 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 {
ProcessEventHost,
ProcessEventContainer,
ProcessEventOrchestrator,
} from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { getHostData, getContainerData, getOrchestratorData } from './helpers';
const MOCK_HOST_DATA: ProcessEventHost = {
architecture: 'x86_64',
hostname: 'james-fleet-714-2',
id: '48c1b3f1ac5da4e0057fc9f60f4d1d5d',
ip: ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'],
mac: ['42:01:0a:84:00:32'],
name: 'james-fleet-714-2',
os: {
family: 'centos',
full: 'CentOS 7.9.2009',
kernel: '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021',
name: 'Linux',
platform: 'centos',
version: '7.9.2009',
},
};
const MOCK_CONTAINER_DATA: ProcessEventContainer = {
id: 'containerd://5fe98d5566148268631302790833b7a14317a2fd212e3e4117bede77d0ca9ba6',
name: 'gce-pd-driver',
image: {
name: 'gke.gcr.io/gcp-compute-persistent-disk-csi-driver',
tag: 'v1.3.5-gke.0',
hash: {
all: 'PLACEHOLDER_FOR_IMAGE.HASH.ALL',
},
},
};
const MOCK_ORCHESTRATOR_DATA: ProcessEventOrchestrator = {
resource: {
name: 'pdcsi-node-6hvsp',
type: 'pod',
ip: 'PLACEHOLDER_FOR_RESOURCE.IP',
},
namespace: 'kube-system',
cluster: {
name: 'elastic-k8s-cluster',
id: 'PLACEHOLDER_FOR_CLUSTER.ID',
},
parent: {
type: 'PLACEHOLDER_FOR_PARENT.TYPE',
},
};
describe('detail panel host tab helpers tests', () => {
it('getHostData returns fields with a dash with undefined host', () => {
const result = getHostData(undefined);
expect(result.architecture).toEqual(DASH);
expect(result.hostname).toEqual(DASH);
expect(result.id).toEqual(DASH);
expect(result.ip).toEqual(DASH);
expect(result.mac).toEqual(DASH);
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(DASH);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(DASH);
expect(result.os.name).toEqual(DASH);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(DASH);
});
it('getHostData returns dashes for missing fields', () => {
const result = getHostData({
...MOCK_HOST_DATA,
ip: ['127.0.0.1', '', '', 'fe80::7d39:3147:4d9a:f809'],
name: undefined,
os: {
...MOCK_HOST_DATA.os,
full: undefined,
platform: undefined,
},
});
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(['127.0.0.1', DASH, DASH, 'fe80::7d39:3147:4d9a:f809'].join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(DASH);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(DASH);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(DASH);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
it('getHostData returns all data provided', () => {
const result = getHostData(MOCK_HOST_DATA);
expect(result.architecture).toEqual(MOCK_HOST_DATA.architecture);
expect(result.hostname).toEqual(MOCK_HOST_DATA.hostname);
expect(result.id).toEqual(MOCK_HOST_DATA.id);
expect(result.ip).toEqual(MOCK_HOST_DATA.ip?.join(', '));
expect(result.mac).toEqual(MOCK_HOST_DATA.mac?.join(', '));
expect(result.name).toEqual(MOCK_HOST_DATA.name);
expect(result.os.family).toEqual(MOCK_HOST_DATA.os?.family);
expect(result.os.full).toEqual(MOCK_HOST_DATA.os?.full);
expect(result.os.kernel).toEqual(MOCK_HOST_DATA.os?.kernel);
expect(result.os.name).toEqual(MOCK_HOST_DATA.os?.name);
expect(result.os.platform).toEqual(MOCK_HOST_DATA.os?.platform);
expect(result.os.version).toEqual(MOCK_HOST_DATA.os?.version);
});
it('getContainerData returns dashes for missing fields', () => {
const result = getContainerData({
id: undefined,
name: 'gce-pd-driver',
image: {
name: undefined,
tag: 'v1.3.5-gke.0',
hash: {
all: undefined,
},
},
});
expect(result.id).toEqual(DASH);
expect(result.name).toEqual(MOCK_CONTAINER_DATA.name);
expect(result.image.name).toEqual(DASH);
expect(result.image.tag).toEqual(MOCK_CONTAINER_DATA?.image?.tag);
expect(result.image.hash.all).toEqual(DASH);
});
it('getContainerData returns all data provided', () => {
const result = getContainerData(MOCK_CONTAINER_DATA);
expect(result.id).toEqual(MOCK_CONTAINER_DATA.id);
expect(result.name).toEqual(MOCK_CONTAINER_DATA.name);
expect(result.image.name).toEqual(MOCK_CONTAINER_DATA?.image?.name);
expect(result.image.tag).toEqual(MOCK_CONTAINER_DATA?.image?.tag);
expect(result.image.hash.all).toEqual(MOCK_CONTAINER_DATA?.image?.hash?.all);
});
it('getOchestratorData returns dashes for missing fields', () => {
const result = getOrchestratorData({
resource: {
name: undefined,
type: 'pod',
ip: undefined,
},
namespace: 'kube-system',
cluster: {
name: 'elastic-k8s-cluster',
id: undefined,
},
parent: {
type: 'PLACEHOLDER_FOR_PARENT.TYPE',
},
});
expect(result.resource.name).toEqual(DASH);
expect(result.resource.type).toEqual(MOCK_ORCHESTRATOR_DATA?.resource?.type);
expect(result.resource.ip).toEqual(DASH);
expect(result.namespace).toEqual(MOCK_ORCHESTRATOR_DATA?.namespace);
expect(result.cluster.name).toEqual(MOCK_ORCHESTRATOR_DATA?.cluster?.name);
expect(result.cluster.id).toEqual(DASH);
expect(result.parent.type).toEqual(MOCK_ORCHESTRATOR_DATA?.parent?.type);
});
it('getOchestratorData returns all data provided', () => {
const result = getOrchestratorData(MOCK_ORCHESTRATOR_DATA);
expect(result.resource.name).toEqual(MOCK_ORCHESTRATOR_DATA?.resource?.name);
expect(result.resource.type).toEqual(MOCK_ORCHESTRATOR_DATA?.resource?.type);
expect(result.resource.ip).toEqual(MOCK_ORCHESTRATOR_DATA?.resource?.ip);
expect(result.namespace).toEqual(MOCK_ORCHESTRATOR_DATA?.namespace);
expect(result.cluster.name).toEqual(MOCK_ORCHESTRATOR_DATA?.cluster?.name);
expect(result.cluster.id).toEqual(MOCK_ORCHESTRATOR_DATA?.cluster?.id);
expect(result.parent.type).toEqual(MOCK_ORCHESTRATOR_DATA?.parent?.type);
});
});

View file

@ -0,0 +1,115 @@
/*
* 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 {
ProcessEventHost,
ProcessEventContainer,
ProcessEventOrchestrator,
} from '../../../common/types/process_tree';
import { DASH } from '../../constants';
import { DetailPanelHost, DetailPanelContainer, DetailPanelOrchestrator } from '../../types';
import { dataOrDash } from '../../utils/data_or_dash';
export const getHostData = (host: ProcessEventHost | undefined): DetailPanelHost => {
const detailPanelHost: DetailPanelHost = {
architecture: DASH,
hostname: DASH,
id: DASH,
ip: DASH,
mac: DASH,
name: DASH,
os: {
family: DASH,
full: DASH,
kernel: DASH,
name: DASH,
platform: DASH,
version: DASH,
},
};
if (!host) {
return detailPanelHost;
}
detailPanelHost.hostname = dataOrDash(host.hostname).toString();
detailPanelHost.id = dataOrDash(host.id).toString();
detailPanelHost.ip = host.ip?.map?.((ip) => dataOrDash(ip)).join(', ') ?? DASH;
detailPanelHost.mac = host.mac?.map?.((mac) => dataOrDash(mac)).join(', ') ?? DASH;
detailPanelHost.name = dataOrDash(host.name).toString();
detailPanelHost.architecture = dataOrDash(host.architecture).toString();
detailPanelHost.os.family = dataOrDash(host.os?.family).toString();
detailPanelHost.os.full = dataOrDash(host.os?.full).toString();
detailPanelHost.os.kernel = dataOrDash(host.os?.kernel).toString();
detailPanelHost.os.name = dataOrDash(host.os?.name).toString();
detailPanelHost.os.platform = dataOrDash(host.os?.platform).toString();
detailPanelHost.os.version = dataOrDash(host.os?.version).toString();
return detailPanelHost;
};
export const getContainerData = (
container: ProcessEventContainer | undefined
): DetailPanelContainer => {
const detailPanelContainer: DetailPanelContainer = {
id: DASH,
name: DASH,
image: {
name: DASH,
tag: DASH,
hash: {
all: DASH,
},
},
};
if (!container) {
return detailPanelContainer;
}
detailPanelContainer.id = dataOrDash(container.id).toString();
detailPanelContainer.name = dataOrDash(container.name).toString();
detailPanelContainer.image.name = dataOrDash(container?.image?.name).toString();
detailPanelContainer.image.tag = dataOrDash(container?.image?.tag).toString();
detailPanelContainer.image.hash.all = dataOrDash(container?.image?.hash?.all).toString();
return detailPanelContainer;
};
export const getOrchestratorData = (
orchestrator: ProcessEventOrchestrator | undefined
): DetailPanelOrchestrator => {
const detailPanelOrchestrator: DetailPanelOrchestrator = {
resource: {
name: DASH,
type: DASH,
ip: DASH,
},
namespace: DASH,
cluster: {
name: DASH,
id: DASH,
},
parent: {
type: DASH,
},
};
if (!orchestrator) {
return detailPanelOrchestrator;
}
detailPanelOrchestrator.resource.name = dataOrDash(orchestrator?.resource?.name).toString();
detailPanelOrchestrator.resource.type = dataOrDash(orchestrator?.resource?.type).toString();
detailPanelOrchestrator.resource.ip = dataOrDash(orchestrator?.resource?.ip).toString();
detailPanelOrchestrator.namespace = dataOrDash(orchestrator?.namespace).toString();
detailPanelOrchestrator.cluster.name = dataOrDash(orchestrator?.cluster?.name).toString();
detailPanelOrchestrator.cluster.id = dataOrDash(orchestrator?.cluster?.id).toString();
detailPanelOrchestrator.parent.type = dataOrDash(orchestrator?.parent?.type).toString();
return detailPanelOrchestrator;
};

View file

@ -0,0 +1,206 @@
/*
* 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 { AppContextTestRender, createAppRootMockRenderer } from '../../test';
import {
ProcessEventHost,
ProcessEventContainer,
ProcessEventOrchestrator,
} from '../../../common/types/process_tree';
import { DetailPanelMetadataTab } from '.';
const TEST_ARCHITECTURE = 'x86_64';
const TEST_HOSTNAME = 'host-james-fleet-714-2';
const TEST_ID = '48c1b3f1ac5da4e0057fc9f60f4d1d5d';
const TEST_IP = ['127.0.0.1', '::1', '10.132.0.50', 'fe80::7d39:3147:4d9a:f809'];
const TEST_MAC = ['42:01:0a:84:00:32'];
const TEST_NAME = 'name-james-fleet-714-2';
const TEST_OS_FAMILY = 'family-centos';
const TEST_OS_FULL = 'full-CentOS 7.9.2009';
const TEST_OS_KERNEL = '3.10.0-1160.31.1.el7.x86_64 #1 SMP Thu Jun 10 13:32:12 UTC 2021';
const TEST_OS_NAME = 'os-Linux';
const TEST_OS_PLATFORM = 'platform-centos';
const TEST_OS_VERSION = 'version-7.9.2009';
// Container data
const TEST_CONTAINER_ID =
'containerd://5fe98d5566148268631302790833b7a14317a2fd212e3e4117bede77d0ca9ba6';
const TEST_CONTAINER_NAME = 'gce-pd-driver';
const TEST_CONTAINER_IMAGE_NAME = 'gke.gcr.io/gcp-compute-persistent-disk-csi-driver';
const TEST_CONTAINER_IMAGE_TAG = 'v1.3.5-gke.0';
const TEST_CONTAINER_IMAGE_HASH_ALL = 'PLACEHOLDER_FOR_IMAGE.HASH.ALL';
// Orchestrator data
const TEST_ORCHESTRATOR_RESOURCE_NAME = 'pdcsi-node-6hvsp';
const TEST_ORCHESTRATOR_RESOURCE_TYPE = 'pod';
const TEST_ORCHESTRATOR_RESOURCE_IP = 'PLACEHOLDER_FOR_RESOURCE.IP';
const TEST_ORCHESTRATOR_NAMESPACE = 'kube-system';
const TEST_ORCHESTRATOR_PARENT_TYPE = 'elastic-k8s-cluster';
const TEST_ORCHESTRATOR_CLUSTER_ID = 'PLACEHOLDER_FOR_CLUSTER.ID';
const TEST_ORCHESTRATOR_CLUSTER_NAME = 'PLACEHOLDER_FOR_PARENT.TYPE';
const TEST_HOST: ProcessEventHost = {
architecture: TEST_ARCHITECTURE,
hostname: TEST_HOSTNAME,
id: TEST_ID,
ip: TEST_IP,
mac: TEST_MAC,
name: TEST_NAME,
os: {
family: TEST_OS_FAMILY,
full: TEST_OS_FULL,
kernel: TEST_OS_KERNEL,
name: TEST_OS_NAME,
platform: TEST_OS_PLATFORM,
version: TEST_OS_VERSION,
},
};
const TEST_CONTAINER: ProcessEventContainer = {
id: TEST_CONTAINER_ID,
name: TEST_CONTAINER_NAME,
image: {
name: TEST_CONTAINER_IMAGE_NAME,
tag: TEST_CONTAINER_IMAGE_TAG,
hash: {
all: TEST_CONTAINER_IMAGE_HASH_ALL,
},
},
};
const TEST_ORCHESTRATOR: ProcessEventOrchestrator = {
resource: {
name: TEST_ORCHESTRATOR_RESOURCE_NAME,
type: TEST_ORCHESTRATOR_RESOURCE_TYPE,
ip: TEST_ORCHESTRATOR_RESOURCE_IP,
},
namespace: TEST_ORCHESTRATOR_NAMESPACE,
cluster: {
name: TEST_ORCHESTRATOR_CLUSTER_NAME,
id: TEST_ORCHESTRATOR_CLUSTER_ID,
},
parent: {
type: TEST_ORCHESTRATOR_PARENT_TYPE,
},
};
describe('DetailPanelMetadataTab component', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let mockedContext: AppContextTestRender;
beforeEach(() => {
mockedContext = createAppRootMockRenderer();
});
describe('When DetailPanelMetadataTab is mounted', () => {
it('renders DetailPanelMetadataTab correctly (non cloud environment)', async () => {
renderResult = mockedContext.render(<DetailPanelMetadataTab processHost={TEST_HOST} />);
expect(renderResult.queryByText('architecture')).toBeVisible();
expect(renderResult.queryByText('hostname')).toBeVisible();
expect(renderResult.queryAllByText('id').length).toBe(1);
expect(renderResult.queryByText('ip')).toBeVisible();
expect(renderResult.queryByText('mac')).toBeVisible();
expect(renderResult.queryAllByText('name').length).toBe(1);
expect(renderResult.queryByText(TEST_ARCHITECTURE)).toBeVisible();
expect(renderResult.queryByText(TEST_HOSTNAME)).toBeVisible();
expect(renderResult.queryByText(TEST_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_IP.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_MAC.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_NAME)).toBeVisible();
// expand host os accordion
renderResult.queryByText('Host OS')?.querySelector('button')?.click();
expect(renderResult.queryByText('os.family')).toBeVisible();
expect(renderResult.queryByText('os.full')).toBeVisible();
expect(renderResult.queryByText('os.kernel')).toBeVisible();
expect(renderResult.queryByText('os.name')).toBeVisible();
expect(renderResult.queryByText('os.platform')).toBeVisible();
expect(renderResult.queryByText('os.version')).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FAMILY)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FULL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_KERNEL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_PLATFORM)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_VERSION)).toBeVisible();
// Orchestrator and Container should be missing if session came from a Non-cloud env
expect(renderResult.queryByText('Container')).toBeNull();
expect(renderResult.queryByText('Orchestrator')).toBeNull();
});
it('renders DetailPanelMetadataTab correctly (cloud environment)', async () => {
renderResult = mockedContext.render(
<DetailPanelMetadataTab
processHost={TEST_HOST}
processContainer={TEST_CONTAINER}
processOrchestrator={TEST_ORCHESTRATOR}
/>
);
expect(renderResult.queryByText('architecture')).toBeVisible();
expect(renderResult.queryByText('hostname')).toBeVisible();
expect(renderResult.queryByText('ip')).toBeVisible();
expect(renderResult.queryByText('mac')).toBeVisible();
expect(renderResult.queryByText(TEST_ARCHITECTURE)).toBeVisible();
expect(renderResult.queryByText(TEST_HOSTNAME)).toBeVisible();
expect(renderResult.queryByText(TEST_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_IP.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_MAC.join(', '))).toBeVisible();
expect(renderResult.queryByText(TEST_NAME)).toBeVisible();
// Checks for existence of id and name fields in Host and Container accordion
expect(renderResult.queryAllByText('id').length).toBe(2);
expect(renderResult.queryAllByText('name').length).toBe(2);
// expand host os accordion
renderResult.queryByText('Host OS')?.querySelector('button')?.click();
expect(renderResult.queryByText('os.family')).toBeVisible();
expect(renderResult.queryByText('os.full')).toBeVisible();
expect(renderResult.queryByText('os.kernel')).toBeVisible();
expect(renderResult.queryByText('os.name')).toBeVisible();
expect(renderResult.queryByText('os.platform')).toBeVisible();
expect(renderResult.queryByText('os.version')).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FAMILY)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_FULL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_KERNEL)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_PLATFORM)).toBeVisible();
expect(renderResult.queryByText(TEST_OS_VERSION)).toBeVisible();
// expand Container Accordion
renderResult.queryByText('Container')?.querySelector('button')?.click();
expect(renderResult.queryByText('image.name')).toBeVisible();
expect(renderResult.queryByText('image.tag')).toBeVisible();
expect(renderResult.queryByText('image.hash.all')).toBeVisible();
expect(renderResult.queryByText(TEST_CONTAINER_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_CONTAINER_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_CONTAINER_IMAGE_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_CONTAINER_IMAGE_TAG)).toBeVisible();
expect(renderResult.queryByText(TEST_CONTAINER_IMAGE_HASH_ALL)).toBeVisible();
// expand Orchestrator Accordion
renderResult.queryByText('Orchestrator')?.querySelector('button')?.click();
expect(renderResult.queryByText('resource.name')).toBeVisible();
expect(renderResult.queryByText('resource.type')).toBeVisible();
expect(renderResult.queryByText('resource.ip')).toBeVisible();
expect(renderResult.queryByText('namespace')).toBeVisible();
expect(renderResult.queryByText('parent.type')).toBeVisible();
expect(renderResult.queryByText('cluster.id')).toBeVisible();
expect(renderResult.queryByText('cluster.name')).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_RESOURCE_NAME)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_RESOURCE_TYPE)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_RESOURCE_IP)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_NAMESPACE)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_PARENT_TYPE)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_CLUSTER_ID)).toBeVisible();
expect(renderResult.queryByText(TEST_ORCHESTRATOR_CLUSTER_NAME)).toBeVisible();
});
});
});

View file

@ -0,0 +1,413 @@
/*
* 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, { useMemo } from 'react';
import { EuiTextColor, EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import {
ProcessEventHost,
ProcessEventContainer,
ProcessEventOrchestrator,
} from '../../../common/types/process_tree';
import { DetailPanelAccordion } from '../detail_panel_accordion';
import { DetailPanelCopy } from '../detail_panel_copy';
import { DetailPanelListItem } from '../detail_panel_list_item';
import { useStyles } from '../detail_panel_process_tab/styles';
import { useStyles as useStylesChild } from './styles';
import { getHostData, getContainerData, getOrchestratorData } from './helpers';
interface DetailPanelMetadataTabDeps {
processHost?: ProcessEventHost;
processContainer?: ProcessEventContainer;
processOrchestrator?: ProcessEventOrchestrator;
}
/**
* Host Panel of session view detail panel.
*/
export const DetailPanelMetadataTab = ({
processHost,
processContainer,
processOrchestrator,
}: DetailPanelMetadataTabDeps) => {
const styles = useStyles();
const stylesChild = useStylesChild();
const hostData = useMemo(() => getHostData(processHost), [processHost]);
const containerData = useMemo(() => getContainerData(processContainer), [processContainer]);
const orchestratorData = useMemo(
() => getOrchestratorData(processOrchestrator),
[processOrchestrator]
);
return (
<>
<DetailPanelAccordion
id="metadataHost"
title={i18n.translate('xpack.sessionView.metadataDetailsTab.metadata', {
defaultMessage: 'Metadata',
})}
initialIsOpen={true}
listItems={[
{
title: <DetailPanelListItem>hostname</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.hostname: "${hostData.hostname}"`}
tooltipContent={hostData.hostname}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.hostname}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.id: "${hostData.id}"`}
tooltipContent={hostData.id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.id}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>ip</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.ip: "${hostData.ip}"`}
tooltipContent={hostData.ip}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.ip}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>mac</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.mac: "${hostData.mac}"`}
tooltipContent={hostData.mac}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.mac}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.name: "${hostData.name}"`}
tooltipContent={hostData.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
>
<EuiPanel
hasShadow={false}
color="subdued"
hasBorder={true}
borderRadius="m"
paddingSize="none"
css={stylesChild.metadataHostOS}
>
<DetailPanelAccordion
id="hostOS"
title={i18n.translate('xpack.sessionView.metadataDetailsTab.host', {
defaultMessage: 'Host OS',
})}
listItems={[
{
title: <DetailPanelListItem>architecture</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.architecture: "${hostData.architecture}"`}
tooltipContent={hostData.architecture}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.architecture}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.family</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.family: "${hostData.os.family}"`}
tooltipContent={hostData.os.family}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.family}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.full</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.full: "${hostData.os.full}"`}
tooltipContent={hostData.os.full}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.full}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.kernel</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.kernel: "${hostData.os.kernel}"`}
tooltipContent={hostData.os.kernel}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.kernel}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.name: "${hostData.os.name}"`}
tooltipContent={hostData.os.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.platform</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.platform: "${hostData.os.platform}"`}
tooltipContent={hostData.os.platform}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.platform}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>os.version</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`host.os.version: "${hostData.os.version}"`}
tooltipContent={hostData.os.version}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{hostData.os.version}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
/>
</EuiPanel>
</DetailPanelAccordion>
{processContainer && (
<>
<DetailPanelAccordion
id="metadataContainer"
title={i18n.translate('xpack.sessionView.metadataDetailsTab.container', {
defaultMessage: 'Container',
})}
listItems={[
{
title: <DetailPanelListItem>id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`container.id: "${containerData.id}"`}
tooltipContent={containerData.id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{containerData.id}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`container.name: "${containerData.name}"`}
tooltipContent={containerData.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{containerData.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>image.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`container.image.name: "${containerData.image.name}"`}
tooltipContent={containerData.image.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{containerData.image.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>image.tag</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`container.image.tag: "${containerData.image.tag}"`}
tooltipContent={containerData.image.tag}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{containerData.image.tag}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>image.hash.all</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`container.image.hash.all: "${containerData.image.hash.all}"`}
tooltipContent={containerData.image.hash.all}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{containerData.image.hash.all}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
/>
</>
)}
{processOrchestrator && (
<>
<DetailPanelAccordion
id="metadataOrchestrator"
title={i18n.translate('xpack.sessionView.metadataDetailsTab.orchestrator', {
defaultMessage: 'Orchestrator',
})}
listItems={[
{
title: <DetailPanelListItem>resource.ip</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.resource.ip: "${orchestratorData.resource.ip}"`}
tooltipContent={orchestratorData.resource.ip}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.resource.ip}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>resource.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.resource.name: "${orchestratorData.resource.name}"`}
tooltipContent={orchestratorData.resource.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.resource.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>resource.type</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.resource.type: "${orchestratorData.resource.type}"`}
tooltipContent={orchestratorData.resource.type}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.resource.type}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>namespace</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.namespace: "${orchestratorData.namespace}"`}
tooltipContent={orchestratorData.namespace}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.namespace}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>cluster.id</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.cluster.id: "${orchestratorData.cluster.id}"`}
tooltipContent={orchestratorData.cluster.id}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.cluster.id}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>cluster.name</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.cluster.name: "${orchestratorData.cluster.name}"`}
tooltipContent={orchestratorData.cluster.name}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.cluster.name}
</EuiTextColor>
</DetailPanelCopy>
),
},
{
title: <DetailPanelListItem>parent.type</DetailPanelListItem>,
description: (
<DetailPanelCopy
textToCopy={`orchestrator.parent.type: "${orchestratorData.parent.type}"`}
tooltipContent={orchestratorData.parent.type}
>
<EuiTextColor color="subdued" css={styles.descriptionSemibold}>
{orchestratorData.parent.type}
</EuiTextColor>
</DetailPanelCopy>
),
},
]}
/>
</>
)}
</>
);
};

View file

@ -0,0 +1,27 @@
/*
* 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 { useMemo } from 'react';
import { useEuiTheme } from '@elastic/eui';
import { CSSObject } from '@emotion/react';
export const useStyles = () => {
const { euiTheme } = useEuiTheme();
const cached = useMemo(() => {
const metadataHostOS: CSSObject = {
margin: `${euiTheme.size.m} ${euiTheme.size.base} ${euiTheme.size.base} ${euiTheme.size.base}`,
paddingBottom: euiTheme.size.base,
};
return {
metadataHostOS,
};
}, [euiTheme]);
return cached;
};

View file

@ -110,7 +110,7 @@ describe('SessionView component', () => {
userEvent.click(renderResult.getByTestId('sessionView:sessionViewDetailPanelToggle'));
expect(renderResult.getByText('Process')).toBeTruthy();
expect(renderResult.getByText('Host')).toBeTruthy();
expect(renderResult.getByText('Metadata')).toBeTruthy();
expect(renderResult.getByText('Alerts')).toBeTruthy();
});

View file

@ -58,7 +58,7 @@ describe('SessionView component', () => {
/>
);
renderResult.queryByText('Host')?.click();
renderResult.queryByText('Metadata')?.click();
expect(renderResult.queryByText('hostname')).toBeVisible();
expect(renderResult.queryAllByText('james-fleet-714-2')).toHaveLength(2);
});

View file

@ -11,7 +11,7 @@ import { EuiTabProps } from '../../types';
import { Process, ProcessEvent } from '../../../common/types/process_tree';
import { getSelectedTabContent } from './helpers';
import { DetailPanelProcessTab } from '../detail_panel_process_tab';
import { DetailPanelHostTab } from '../detail_panel_host_tab';
import { DetailPanelMetadataTab } from '../detail_panel_metadata_tab';
import { useStyles } from './styles';
import { DetailPanelAlertTab } from '../detail_panel_alert_tab';
import { ALERT_COUNT_THRESHOLD } from '../../../common/constants';
@ -56,11 +56,17 @@ export const SessionViewDetailPanel = ({
content: <DetailPanelProcessTab selectedProcess={selectedProcess} />,
},
{
id: 'host',
name: i18n.translate('xpack.sessionView.detailsPanel.host', {
defaultMessage: 'Host',
id: 'metadata',
name: i18n.translate('xpack.sessionView.detailsPanel.metadata', {
defaultMessage: 'Metadata',
}),
content: <DetailPanelHostTab processHost={selectedProcess?.events[0]?.host} />,
content: (
<DetailPanelMetadataTab
processHost={selectedProcess?.events[0]?.host}
processContainer={selectedProcess?.events[0]?.container}
processOrchestrator={selectedProcess?.events[0]?.orchestrator}
/>
),
},
{
id: 'alerts',

View file

@ -90,6 +90,34 @@ export interface DetailPanelHost {
};
}
export interface DetailPanelContainer {
id: string;
name: string;
image: {
name: string;
tag: string;
hash: {
all: string;
};
};
}
export interface DetailPanelOrchestrator {
resource: {
name: string;
type: string;
ip: string;
};
namespace: string;
cluster: {
name: string;
id: string;
};
parent: {
type: string;
};
}
export interface SessionViewStart {
getSessionView: (props: SessionViewDeps) => JSX.Element;
}

View file

@ -26853,7 +26853,6 @@
"xpack.sessionView.detailPanelAlertsEmptyTitle": "Aucune alerte",
"xpack.sessionView.detailPanelCopy.copyButton": "Copier",
"xpack.sessionView.detailsPanel.alerts": "Alertes",
"xpack.sessionView.detailsPanel.host": "Hôte",
"xpack.sessionView.detailsPanel.process": "Processus",
"xpack.sessionView.emptyDataMessage": "Aucun événement de processus n'a été trouvé pour cette requête.",
"xpack.sessionView.emptyDataTitle": "Aucune donnée à rendre",

View file

@ -27022,7 +27022,6 @@
"xpack.sessionView.detailPanelAlertsEmptyTitle": "アラートなし",
"xpack.sessionView.detailPanelCopy.copyButton": "コピー",
"xpack.sessionView.detailsPanel.alerts": "アラート",
"xpack.sessionView.detailsPanel.host": "ホスト",
"xpack.sessionView.detailsPanel.process": "プロセス",
"xpack.sessionView.emptyDataMessage": "このクエリのプロセスイベントが見つかりません。",
"xpack.sessionView.emptyDataTitle": "表示するデータがありません",

View file

@ -27056,7 +27056,6 @@
"xpack.sessionView.detailPanelAlertsEmptyTitle": "无告警",
"xpack.sessionView.detailPanelCopy.copyButton": "复制",
"xpack.sessionView.detailsPanel.alerts": "告警",
"xpack.sessionView.detailsPanel.host": "主机",
"xpack.sessionView.detailsPanel.process": "进程",
"xpack.sessionView.emptyDataMessage": "找不到此查询的进程事件。",
"xpack.sessionView.emptyDataTitle": "没有可呈现的数据",