[Metrics UI] Add metadata tab to node details flyout (#84454)
* Add properties tab to flyout * Better id for i18n title * Update i18n ids * Fix test and styling * Style changes, add support for collapsing array fields * Add loading indicators * Fix type check * Fix another test * Fix tests for pods * Add link to node details page * Only show the overlay when viewing hosts * Take into account cores when showing cpu * Make it easier to read * Remove unnecessary cast * Fix PR feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
74ef540e42
commit
61fdec2486
|
@ -29,10 +29,15 @@ export const InfraMetadataOSRT = rt.partial({
|
|||
name: rt.string,
|
||||
platform: rt.string,
|
||||
version: rt.string,
|
||||
build: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataHostRT = rt.partial({
|
||||
name: rt.string,
|
||||
hostname: rt.string,
|
||||
id: rt.string,
|
||||
ip: rt.array(rt.string),
|
||||
mac: rt.array(rt.string),
|
||||
os: InfraMetadataOSRT,
|
||||
architecture: rt.string,
|
||||
containerized: rt.boolean,
|
||||
|
@ -43,25 +48,40 @@ export const InfraMetadataInstanceRT = rt.partial({
|
|||
name: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataAccountRT = rt.partial({
|
||||
id: rt.string,
|
||||
name: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataProjectRT = rt.partial({
|
||||
id: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataMachineRT = rt.partial({
|
||||
interface: rt.string,
|
||||
type: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataCloudRT = rt.partial({
|
||||
instance: InfraMetadataInstanceRT,
|
||||
provider: rt.string,
|
||||
account: InfraMetadataAccountRT,
|
||||
availability_zone: rt.string,
|
||||
project: InfraMetadataProjectRT,
|
||||
machine: InfraMetadataMachineRT,
|
||||
region: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataAgentRT = rt.partial({
|
||||
id: rt.string,
|
||||
version: rt.string,
|
||||
policy: rt.string,
|
||||
});
|
||||
|
||||
export const InfraMetadataInfoRT = rt.partial({
|
||||
cloud: InfraMetadataCloudRT,
|
||||
host: InfraMetadataHostRT,
|
||||
agent: InfraMetadataAgentRT,
|
||||
});
|
||||
|
||||
const InfraMetadataRequiredRT = rt.type({
|
||||
|
|
|
@ -14,8 +14,11 @@ import { InventoryItemType } from '../../../../../../common/inventory_models/typ
|
|||
import { MetricsTab } from './tabs/metrics/metrics';
|
||||
import { LogsTab } from './tabs/logs';
|
||||
import { ProcessesTab } from './tabs/processes';
|
||||
import { PropertiesTab } from './tabs/properties';
|
||||
import { PropertiesTab } from './tabs/properties/index';
|
||||
import { OVERLAY_Y_START, OVERLAY_BOTTOM_MARGIN, OVERLAY_HEADER_SIZE } from './tabs/shared';
|
||||
import { useLinkProps } from '../../../../../hooks/use_link_props';
|
||||
import { getNodeDetailUrl } from '../../../../link_to';
|
||||
import { findInventoryModel } from '../../../../../../common/inventory_models';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
|
@ -35,6 +38,8 @@ export const NodeContextPopover = ({
|
|||
}: Props) => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const tabConfigs = [MetricsTab, LogsTab, ProcessesTab, PropertiesTab];
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
return tabConfigs.map((m) => {
|
||||
|
@ -50,6 +55,15 @@ export const NodeContextPopover = ({
|
|||
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
||||
const nodeDetailMenuItemLinkProps = useLinkProps({
|
||||
...getNodeDetailUrl({
|
||||
nodeType,
|
||||
nodeId: node.id,
|
||||
from: nodeDetailFrom,
|
||||
to: currentTime,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!isOpen) {
|
||||
return null;
|
||||
}
|
||||
|
@ -65,10 +79,29 @@ export const NodeContextPopover = ({
|
|||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClose} iconType={'cross'}>
|
||||
<FormattedMessage id="xpack.infra.infra.nodeDetails.close" defaultMessage="Close" />
|
||||
<EuiFlexGroup gutterSize={'xs'} alignItems={'flexEnd'}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
iconSide={'left'}
|
||||
iconType={'popout'}
|
||||
href={nodeDetailMenuItemLinkProps.href}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.infra.nodeDetails.openAsPage"
|
||||
defaultMessage="Open as page"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClose} iconType={'cross'}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.infra.nodeDetails.close"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</OverlayHeaderTitleWrapper>
|
||||
<EuiTabs>
|
||||
{tabs.map((tab, i) => (
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
PointerEvent,
|
||||
} from '@elastic/charts';
|
||||
import moment from 'moment';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { TabContent, TabProps } from '../shared';
|
||||
import { useSnapshot } from '../../../../hooks/use_snaphot';
|
||||
import { useWaffleOptionsContext } from '../../../../hooks/use_waffle_options';
|
||||
|
@ -82,9 +83,9 @@ const TabComponent = (props: TabProps) => {
|
|||
}
|
||||
|
||||
const buildCustomMetric = useCallback(
|
||||
(field: string, id: string) => ({
|
||||
(field: string, id: string, aggregation: string = 'avg') => ({
|
||||
type: 'custom' as SnapshotMetricType,
|
||||
aggregation: 'avg',
|
||||
aggregation,
|
||||
field,
|
||||
id,
|
||||
}),
|
||||
|
@ -110,6 +111,7 @@ const TabComponent = (props: TabProps) => {
|
|||
buildCustomMetric('system.load.15', 'load15m'),
|
||||
buildCustomMetric('system.memory.actual.used.bytes', 'usedMemory'),
|
||||
buildCustomMetric('system.memory.actual.free', 'freeMemory'),
|
||||
buildCustomMetric('system.cpu.cores', 'cores', 'max'),
|
||||
],
|
||||
[],
|
||||
nodeType,
|
||||
|
@ -223,6 +225,7 @@ const TabComponent = (props: TabProps) => {
|
|||
const load15mMetricsTs = useMemo(() => getTimeseries('load15m'), [getTimeseries]);
|
||||
const usedMemoryMetricsTs = useMemo(() => getTimeseries('usedMemory'), [getTimeseries]);
|
||||
const freeMemoryMetricsTs = useMemo(() => getTimeseries('freeMemory'), [getTimeseries]);
|
||||
const coresMetricsTs = useMemo(() => getTimeseries('cores'), [getTimeseries]);
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
|
@ -239,7 +242,7 @@ const TabComponent = (props: TabProps) => {
|
|||
!usedMemoryMetricsTs ||
|
||||
!freeMemoryMetricsTs
|
||||
) {
|
||||
return <div />;
|
||||
return <LoadingPlaceholder />;
|
||||
}
|
||||
|
||||
const cpuChartMetrics = buildChartMetricLabels([SYSTEM_METRIC_NAME, USER_METRIC_NAME], 'avg');
|
||||
|
@ -253,6 +256,23 @@ const TabComponent = (props: TabProps) => {
|
|||
'rate'
|
||||
);
|
||||
|
||||
systemMetricsTs.rows = systemMetricsTs.rows.slice().map((r, idx) => {
|
||||
const metric = r.metric_0 as number | undefined;
|
||||
const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
|
||||
if (metric && cores) {
|
||||
r.metric_0 = metric / cores;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
|
||||
userMetricsTs.rows = userMetricsTs.rows.slice().map((r, idx) => {
|
||||
const metric = r.metric_0 as number | undefined;
|
||||
const cores = coresMetricsTs!.rows[idx].metric_0 as number | undefined;
|
||||
if (metric && cores) {
|
||||
r.metric_0 = metric / cores;
|
||||
}
|
||||
return r;
|
||||
});
|
||||
const cpuTimeseries = mergeTimeseries(systemMetricsTs, userMetricsTs);
|
||||
const networkTimeseries = mergeTimeseries(rxMetricsTs, txMetricsTs);
|
||||
const loadTimeseries = mergeTimeseries(load1mMetricsTs, load5mMetricsTs, load15mMetricsTs);
|
||||
|
@ -467,6 +487,23 @@ const ChartContainer: React.FC = ({ children }) => (
|
|||
</div>
|
||||
);
|
||||
|
||||
const LoadingPlaceholder = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<EuiLoadingChart size="xl" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MetricsTab = {
|
||||
id: 'metrics',
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.metrics', {
|
||||
|
|
|
@ -1,21 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TabContent, TabProps } from './shared';
|
||||
|
||||
const TabComponent = (props: TabProps) => {
|
||||
return <TabContent>Properties Placeholder</TabContent>;
|
||||
};
|
||||
|
||||
export const PropertiesTab = {
|
||||
id: 'properties',
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.properties', {
|
||||
defaultMessage: 'Properties',
|
||||
}),
|
||||
content: TabComponent,
|
||||
};
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { InfraMetadata } from '../../../../../../../../common/http_api';
|
||||
|
||||
export const getFields = (metadata: InfraMetadata, group: 'cloud' | 'host' | 'agent') => {
|
||||
switch (group) {
|
||||
case 'host':
|
||||
return prune([
|
||||
{
|
||||
name: 'host.architecture',
|
||||
value: metadata.info?.host?.architecture,
|
||||
},
|
||||
{
|
||||
name: 'host.hostname',
|
||||
value: metadata.info?.host?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.id',
|
||||
value: metadata.info?.host?.id,
|
||||
},
|
||||
{
|
||||
name: 'host.ip',
|
||||
value: metadata.info?.host?.ip,
|
||||
},
|
||||
{
|
||||
name: 'host.mac',
|
||||
value: metadata.info?.host?.mac,
|
||||
},
|
||||
{
|
||||
name: 'host.name',
|
||||
value: metadata.info?.host?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.os.build',
|
||||
value: metadata.info?.host?.os?.build,
|
||||
},
|
||||
{
|
||||
name: 'host.os.family',
|
||||
value: metadata.info?.host?.os?.family,
|
||||
},
|
||||
{
|
||||
name: 'host.os.name',
|
||||
value: metadata.info?.host?.os?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.os.kernel',
|
||||
value: metadata.info?.host?.os?.kernel,
|
||||
},
|
||||
{
|
||||
name: 'host.os.platform',
|
||||
value: metadata.info?.host?.os?.platform,
|
||||
},
|
||||
{
|
||||
name: 'host.os.version',
|
||||
value: metadata.info?.host?.os?.version,
|
||||
},
|
||||
]);
|
||||
case 'cloud':
|
||||
return prune([
|
||||
{
|
||||
name: 'cloud.account.id',
|
||||
value: metadata.info?.cloud?.account?.id,
|
||||
},
|
||||
{
|
||||
name: 'cloud.account.name',
|
||||
value: metadata.info?.cloud?.account?.name,
|
||||
},
|
||||
{
|
||||
name: 'cloud.availability_zone',
|
||||
value: metadata.info?.cloud?.availability_zone,
|
||||
},
|
||||
{
|
||||
name: 'cloud.instance.id',
|
||||
value: metadata.info?.cloud?.instance?.id,
|
||||
},
|
||||
{
|
||||
name: 'cloud.instance.name',
|
||||
value: metadata.info?.cloud?.instance?.name,
|
||||
},
|
||||
{
|
||||
name: 'cloud.machine.type',
|
||||
value: metadata.info?.cloud?.machine?.type,
|
||||
},
|
||||
{
|
||||
name: 'cloud.provider',
|
||||
value: metadata.info?.cloud?.provider,
|
||||
},
|
||||
{
|
||||
name: 'cloud.region',
|
||||
value: metadata.info?.cloud?.region,
|
||||
},
|
||||
]);
|
||||
case 'agent':
|
||||
return prune([
|
||||
{
|
||||
name: 'agent.id',
|
||||
value: metadata.info?.agent?.id,
|
||||
},
|
||||
{
|
||||
name: 'agent.version',
|
||||
value: metadata.info?.agent?.version,
|
||||
},
|
||||
{
|
||||
name: 'agent.policy',
|
||||
value: metadata.info?.agent?.policy,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
const prune = (fields: Array<{ name: string; value: string | string[] | undefined }>) =>
|
||||
fields.filter((f) => !!f.value);
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { TabContent, TabProps } from '../shared';
|
||||
import { Source } from '../../../../../../../containers/source';
|
||||
import { findInventoryModel } from '../../../../../../../../common/inventory_models';
|
||||
import { InventoryItemType } from '../../../../../../../../common/inventory_models/types';
|
||||
import { useMetadata } from '../../../../../metric_detail/hooks/use_metadata';
|
||||
import { getFields } from './build_fields';
|
||||
import { useWaffleTimeContext } from '../../../../hooks/use_waffle_time';
|
||||
import { Table } from './table';
|
||||
import { euiStyled } from '../../../../../../../../../observability/public';
|
||||
import { useWaffleFiltersContext } from '../../../../hooks/use_waffle_filters';
|
||||
|
||||
const TabComponent = (props: TabProps) => {
|
||||
const nodeId = props.node.id;
|
||||
const nodeType = props.nodeType as InventoryItemType;
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
const { sourceId } = useContext(Source.Context);
|
||||
const { currentTimeRange } = useWaffleTimeContext();
|
||||
const { applyFilterQuery } = useWaffleFiltersContext();
|
||||
const { loading: metadataLoading, metadata } = useMetadata(
|
||||
nodeId,
|
||||
nodeType,
|
||||
inventoryModel.requiredMetrics,
|
||||
sourceId,
|
||||
currentTimeRange
|
||||
);
|
||||
|
||||
const hostFields = useMemo(() => {
|
||||
if (!metadata) return null;
|
||||
return getFields(metadata, 'host');
|
||||
}, [metadata]);
|
||||
|
||||
const cloudFields = useMemo(() => {
|
||||
if (!metadata) return null;
|
||||
return getFields(metadata, 'cloud');
|
||||
}, [metadata]);
|
||||
|
||||
const agentFields = useMemo(() => {
|
||||
if (!metadata) return null;
|
||||
return getFields(metadata, 'agent');
|
||||
}, [metadata]);
|
||||
|
||||
const onFilter = useCallback(
|
||||
(item: { name: string; value: string }) => {
|
||||
applyFilterQuery({
|
||||
kind: 'kuery',
|
||||
expression: `${item.name}: "${item.value}"`,
|
||||
});
|
||||
},
|
||||
[applyFilterQuery]
|
||||
);
|
||||
|
||||
if (metadataLoading) {
|
||||
return <LoadingPlaceholder />;
|
||||
}
|
||||
|
||||
return (
|
||||
<TabContent>
|
||||
{hostFields && hostFields.length > 0 && (
|
||||
<TableWrapper>
|
||||
<Table
|
||||
title={i18n.translate('xpack.infra.nodeDetails.tabs.metadata.hostsHeader', {
|
||||
defaultMessage: 'Hosts',
|
||||
})}
|
||||
onClick={onFilter}
|
||||
rows={hostFields}
|
||||
/>
|
||||
</TableWrapper>
|
||||
)}
|
||||
{cloudFields && cloudFields.length > 0 && (
|
||||
<TableWrapper>
|
||||
<Table
|
||||
title={i18n.translate('xpack.infra.nodeDetails.tabs.metadata.cloudHeader', {
|
||||
defaultMessage: 'Cloud',
|
||||
})}
|
||||
onClick={onFilter}
|
||||
rows={cloudFields}
|
||||
/>
|
||||
</TableWrapper>
|
||||
)}
|
||||
{agentFields && agentFields.length > 0 && (
|
||||
<TableWrapper>
|
||||
<Table
|
||||
title={i18n.translate('xpack.infra.nodeDetails.tabs.metadata.agentHeader', {
|
||||
defaultMessage: 'Agent',
|
||||
})}
|
||||
onClick={onFilter}
|
||||
rows={agentFields}
|
||||
/>
|
||||
</TableWrapper>
|
||||
)}
|
||||
</TabContent>
|
||||
);
|
||||
};
|
||||
|
||||
const TableWrapper = euiStyled.div`
|
||||
margin-bottom: 20px
|
||||
`;
|
||||
|
||||
const LoadingPlaceholder = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '200px',
|
||||
padding: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<EuiLoadingChart size="xl" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const PropertiesTab = {
|
||||
id: 'properties',
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
|
||||
defaultMessage: 'Metadata',
|
||||
}),
|
||||
content: TabComponent,
|
||||
};
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
import { EuiButtonIcon } from '@elastic/eui';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { EuiBasicTable } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { first } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { euiStyled } from '../../../../../../../../../observability/public';
|
||||
|
||||
interface Row {
|
||||
name: string;
|
||||
value: string | string[] | undefined;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
rows: Row[];
|
||||
title: string;
|
||||
onClick(item: Row): void;
|
||||
}
|
||||
|
||||
export const Table = (props: Props) => {
|
||||
const { rows, title, onClick } = props;
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
field: 'name',
|
||||
name: '',
|
||||
width: '35%',
|
||||
sortable: false,
|
||||
render: (name: string, item: Row) => (
|
||||
<EuiText size="xs">
|
||||
<strong>{item.name}</strong>
|
||||
</EuiText>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
name: '',
|
||||
width: '65%',
|
||||
sortable: false,
|
||||
render: (_name: string, item: Row) => {
|
||||
return (
|
||||
<span>
|
||||
<EuiToolTip
|
||||
content={i18n.translate('xpack.infra.nodeDetails.tabs.metadata.setFilterTooltip', {
|
||||
defaultMessage: 'View event with filter',
|
||||
})}
|
||||
>
|
||||
<EuiFlexGroup gutterSize={'xs'}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonIcon
|
||||
color="text"
|
||||
iconType="filter"
|
||||
aria-label={i18n.translate(
|
||||
'xpack.infra.nodeDetails.tabs.metadata.filterAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Filter',
|
||||
}
|
||||
)}
|
||||
onClick={() => onClick(item)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{!Array.isArray(item.value) && item.value}
|
||||
{Array.isArray(item.value) && <ArrayValue values={item.value} />}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiToolTip>
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[onClick]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<TitleWrapper>
|
||||
<EuiText>
|
||||
<h3>{title}</h3>
|
||||
</EuiText>
|
||||
</TitleWrapper>
|
||||
<TableWithoutHeader tableLayout="fixed" compressed columns={columns} items={rows} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const TitleWrapper = euiStyled.div`
|
||||
margin-bottom: 10px
|
||||
`;
|
||||
|
||||
class TableWithoutHeader extends EuiBasicTable {
|
||||
renderTableHead() {
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
|
||||
interface MoreProps {
|
||||
values: string[];
|
||||
}
|
||||
const ArrayValue = (props: MoreProps) => {
|
||||
const { values } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const expand = useCallback(() => {
|
||||
setIsExpanded(true);
|
||||
}, []);
|
||||
|
||||
const collapse = useCallback(() => {
|
||||
setIsExpanded(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isExpanded && (
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={false}>
|
||||
{first(values)}
|
||||
{' ... '}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiLink onClick={expand}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.nodeDetails.tabs.metadata.seeMore"
|
||||
defaultMessage="+{count} more"
|
||||
values={{
|
||||
count: values.length,
|
||||
}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
{isExpanded && (
|
||||
<div>
|
||||
{values.map((v) => (
|
||||
<div key={v}>{v}</div>
|
||||
))}
|
||||
<EuiLink onClick={collapse}>
|
||||
{i18n.translate('xpack.infra.nodeDetails.tabs.metadata.seeLess', {
|
||||
defaultMessage: 'See less',
|
||||
})}
|
||||
</EuiLink>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -54,6 +54,7 @@ export const Node = class extends React.PureComponent<Props, State> {
|
|||
defaultMessage: '{nodeName}, click to open menu',
|
||||
values: { nodeName: node.name },
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeContextMenu
|
||||
|
@ -102,7 +103,12 @@ export const Node = class extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private togglePopover = () => {
|
||||
const { nodeType } = this.props;
|
||||
if (nodeType === 'host') {
|
||||
this.toggleNewOverlay();
|
||||
} else {
|
||||
this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen }));
|
||||
}
|
||||
};
|
||||
|
||||
private toggleNewOverlay = () => {
|
||||
|
|
|
@ -161,14 +161,6 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
},
|
||||
};
|
||||
|
||||
const openNewOverlayMenuItem: SectionLinkProps = {
|
||||
label: i18n.translate('xpack.infra.nodeContextMenu.openNewOverlay', {
|
||||
defaultMessage: '**** [NEW] Overlay ***',
|
||||
}),
|
||||
style: { color: theme?.eui.euiLinkColor || '#006BB4', fontWeight: 500, padding: 0 },
|
||||
onClick: openNewOverlay,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionMenu
|
||||
|
@ -204,7 +196,6 @@ export const NodeContextMenu: React.FC<Props & { theme?: EuiTheme }> = withTheme
|
|||
<SectionLink data-test-subj="viewApmTracesContextMenuItem" {...apmTracesMenuItem} />
|
||||
<SectionLink {...uptimeMenuItem} />
|
||||
<SectionLink {...createAlertMenuItem} />
|
||||
<SectionLink {...openNewOverlayMenuItem} />
|
||||
</SectionLinks>
|
||||
</Section>
|
||||
</div>
|
||||
|
|
|
@ -58,7 +58,7 @@ export const getNodeInfo = async (
|
|||
index: sourceConfiguration.metricAlias,
|
||||
body: {
|
||||
size: 1,
|
||||
_source: ['host.*', 'cloud.*'],
|
||||
_source: ['host.*', 'cloud.*', 'agent.*'],
|
||||
sort: [{ [timestampField]: 'desc' }],
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -109,6 +109,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
machine: { type: 'n1-standard-4' },
|
||||
project: { id: 'elastic-observability' },
|
||||
},
|
||||
agent: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-f0zc',
|
||||
id: 'c91c0d2b-6483-46bb-9731-f06afd32bb59',
|
||||
ephemeral_id: '7cb259b1-795c-4c76-beaf-2eb8f18f5b02',
|
||||
type: 'metricbeat',
|
||||
version: '8.0.0',
|
||||
},
|
||||
host: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-f0zc',
|
||||
os: {
|
||||
|
@ -150,6 +157,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
region: 'us-east-2',
|
||||
account: { id: '015351775590' },
|
||||
},
|
||||
agent: {
|
||||
hostname: 'ip-172-31-47-9.us-east-2.compute.internal',
|
||||
id: 'd0943b36-d0d3-426d-892b-7d79c071b44b',
|
||||
ephemeral_id: '64c94244-88b8-4a37-adc0-30428fefaf53',
|
||||
type: 'metricbeat',
|
||||
version: '8.0.0',
|
||||
},
|
||||
host: {
|
||||
hostname: 'ip-172-31-47-9.us-east-2.compute.internal',
|
||||
os: {
|
||||
|
@ -197,6 +211,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'elastic-observability',
|
||||
},
|
||||
},
|
||||
agent: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-ngmh',
|
||||
id: '66dc19e6-da36-49d2-9471-2c9475503178',
|
||||
ephemeral_id: 'a0c3a9ff-470a-41a0-bf43-d1af6b7a3b5b',
|
||||
type: 'metricbeat',
|
||||
version: '8.0.0',
|
||||
},
|
||||
host: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-ngmh',
|
||||
name: 'gke-observability-8--observability-8--bc1afd95-ngmh',
|
||||
|
@ -244,6 +265,13 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
id: 'elastic-observability',
|
||||
},
|
||||
},
|
||||
agent: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-nhhw',
|
||||
id: 'c58a514c-e971-4590-8206-385400e184dd',
|
||||
ephemeral_id: 'e9d46cb0-2e89-469d-bd3b-6f32d7c96cc0',
|
||||
type: 'metricbeat',
|
||||
version: '8.0.0',
|
||||
},
|
||||
host: {
|
||||
hostname: 'gke-observability-8--observability-8--bc1afd95-nhhw',
|
||||
name: 'gke-observability-8--observability-8--bc1afd95-nhhw',
|
||||
|
|
Loading…
Reference in a new issue