diff --git a/packages/kbn-apm-config-loader/src/config.ts b/packages/kbn-apm-config-loader/src/config.ts
index a611e205ec83..6e5a830d04b1 100644
--- a/packages/kbn-apm-config-loader/src/config.ts
+++ b/packages/kbn-apm-config-loader/src/config.ts
@@ -27,22 +27,27 @@ import { ApmAgentConfig } from './types';
const getDefaultConfig = (isDistributable: boolean): ApmAgentConfig => {
// https://www.elastic.co/guide/en/apm/agent/nodejs/current/configuration.html
+
return {
- active: process.env.ELASTIC_APM_ACTIVE || false,
+ active: process.env.ELASTIC_APM_ACTIVE === 'true' || false,
environment: process.env.ELASTIC_APM_ENVIRONMENT || process.env.NODE_ENV || 'development',
- serverUrl: 'https://b1e3b4b4233e44cdad468c127d0af8d8.apm.europe-west1.gcp.cloud.es.io:443',
+ serverUrl: 'https://38b80fbd79fb4c91bae06b4642d4d093.apm.us-east-1.aws.cloud.es.io',
// The secretToken below is intended to be hardcoded in this file even though
// it makes it public. This is not a security/privacy issue. Normally we'd
// instead disable the need for a secretToken in the APM Server config where
// the data is transmitted to, but due to how it's being hosted, it's easier,
// for now, to simply leave it in.
- secretToken: '2OyjjaI6RVkzx2O5CV',
+ secretToken: 'ZQHYvrmXEx04ozge8F',
logUncaughtExceptions: true,
globalLabels: {},
centralConfig: false,
+ metricsInterval: isDistributable ? '120s' : '30s',
+ transactionSampleRate: process.env.ELASTIC_APM_TRANSACTION_SAMPLE_RATE
+ ? parseFloat(process.env.ELASTIC_APM_TRANSACTION_SAMPLE_RATE)
+ : 1.0,
// Can be performance intensive, disabling by default
breakdownMetrics: isDistributable ? false : true,
@@ -150,8 +155,9 @@ export class ApmConfiguration {
globalLabels: {
branch: process.env.ghprbSourceBranch || '',
targetBranch: process.env.ghprbTargetBranch || '',
- ciJobName: process.env.JOB_NAME || '',
ciBuildNumber: process.env.BUILD_NUMBER || '',
+ isPr: process.env.GITHUB_PR_NUMBER ? true : false,
+ prId: process.env.GITHUB_PR_NUMBER || '',
},
};
}
diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy
index 005129370471..7991dd325215 100644
--- a/vars/kibanaPipeline.groovy
+++ b/vars/kibanaPipeline.groovy
@@ -89,6 +89,7 @@ def withFunctionalTestEnv(List additionalEnvs = [], Closure closure) {
def esTransportPort = "61${parallelId}3"
def fleetPackageRegistryPort = "61${parallelId}4"
def alertingProxyPort = "61${parallelId}5"
+ def apmActive = githubPr.isPr() ? "false" : "true"
withEnv([
"CI_GROUP=${parallelId}",
@@ -101,7 +102,9 @@ def withFunctionalTestEnv(List additionalEnvs = [], Closure closure) {
"TEST_ES_TRANSPORT_PORT=${esTransportPort}",
"KBN_NP_PLUGINS_BUILT=true",
"FLEET_PACKAGE_REGISTRY_PORT=${fleetPackageRegistryPort}",
- "ALERTING_PROXY_PORT=${alertingProxyPort}"
+ "ALERTING_PROXY_PORT=${alertingProxyPort}",
+ "ELASTIC_APM_ACTIVE=${apmActive}",
+ "ELASTIC_APM_TRANSACTION_SAMPLE_RATE=0.1",
] + additionalEnvs) {
closure()
}
diff --git a/x-pack/plugins/infra/common/http_api/metadata_api.ts b/x-pack/plugins/infra/common/http_api/metadata_api.ts
index 5ee96b479be8..41b599c31041 100644
--- a/x-pack/plugins/infra/common/http_api/metadata_api.ts
+++ b/x-pack/plugins/infra/common/http_api/metadata_api.ts
@@ -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({
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
index 0943ced5e5be..be953ded70d7 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/overlay.tsx
@@ -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,9 +79,28 @@ export const NodeContextPopover = ({
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
index b5628b0a7c9b..789658c06040 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/metrics/metrics.tsx
@@ -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 ;
+ return ;
}
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 }) => (
);
+const LoadingPlaceholder = () => {
+ return (
+
+
+
+ );
+};
+
export const MetricsTab = {
id: 'metrics',
name: i18n.translate('xpack.infra.nodeDetails.tabs.metrics', {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx
deleted file mode 100644
index 8157aca9b141..000000000000
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties.tsx
+++ /dev/null
@@ -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 Properties Placeholder;
-};
-
-export const PropertiesTab = {
- id: 'properties',
- name: i18n.translate('xpack.infra.nodeDetails.tabs.properties', {
- defaultMessage: 'Properties',
- }),
- content: TabComponent,
-};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts
new file mode 100644
index 000000000000..79610ba3eef0
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/build_fields.ts
@@ -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);
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
new file mode 100644
index 000000000000..b901c3748438
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/index.tsx
@@ -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 ;
+ }
+
+ return (
+
+ {hostFields && hostFields.length > 0 && (
+
+
+
+ )}
+ {cloudFields && cloudFields.length > 0 && (
+
+
+
+ )}
+ {agentFields && agentFields.length > 0 && (
+
+
+
+ )}
+
+ );
+};
+
+const TableWrapper = euiStyled.div`
+ margin-bottom: 20px
+`;
+
+const LoadingPlaceholder = () => {
+ return (
+
+
+
+ );
+};
+
+export const PropertiesTab = {
+ id: 'properties',
+ name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
+ defaultMessage: 'Metadata',
+ }),
+ content: TabComponent,
+};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx
new file mode 100644
index 000000000000..c3e47b6084eb
--- /dev/null
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/node_details/tabs/properties/table.tsx
@@ -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) => (
+
+ {item.name}
+
+ ),
+ },
+ {
+ field: 'value',
+ name: '',
+ width: '65%',
+ sortable: false,
+ render: (_name: string, item: Row) => {
+ return (
+
+
+
+
+ onClick(item)}
+ />
+
+
+ {!Array.isArray(item.value) && item.value}
+ {Array.isArray(item.value) && }
+
+
+
+
+ );
+ },
+ },
+ ],
+ [onClick]
+ );
+
+ return (
+ <>
+
+
+ {title}
+
+
+
+ >
+ );
+};
+
+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 && (
+
+
+ {first(values)}
+ {' ... '}
+
+
+
+
+
+
+
+ )}
+ {isExpanded && (
+
+ {values.map((v) => (
+
{v}
+ ))}
+
+ {i18n.translate('xpack.infra.nodeDetails.tabs.metadata.seeLess', {
+ defaultMessage: 'See less',
+ })}
+
+
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
index f2d9da960df8..03fb53898e31 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx
@@ -54,6 +54,7 @@ export const Node = class extends React.PureComponent {
defaultMessage: '{nodeName}, click to open menu',
values: { nodeName: node.name },
});
+
return (
<>
{
}
private togglePopover = () => {
- this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen }));
+ const { nodeType } = this.props;
+ if (nodeType === 'host') {
+ this.toggleNewOverlay();
+ } else {
+ this.setState((prevState) => ({ isPopoverOpen: !prevState.isPopoverOpen }));
+ }
};
private toggleNewOverlay = () => {
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
index 91c6ad801000..3179d4aa0526 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx
@@ -161,14 +161,6 @@ export const NodeContextMenu: React.FC = 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 (
<>
= withTheme
-
diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
index f1341c7ec810..b378b42e2ff5 100644
--- a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
+++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts
@@ -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: {
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx
index e66534ae1b25..706a2c47a348 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/common_fields/common_processor_fields.tsx
@@ -6,6 +6,7 @@
import React, { FunctionComponent } from 'react';
import { i18n } from '@kbn/i18n';
+import { PainlessLang } from '@kbn/monaco';
import {
FieldConfig,
@@ -56,6 +57,8 @@ const tagConfig: FieldConfig = {
};
export const CommonProcessorFields: FunctionComponent = () => {
+ const suggestionProvider = PainlessLang.getSuggestionProvider('processor_conditional');
+
return (
{
component={TextEditor}
componentProps={{
editorProps: {
- languageId: 'painless',
+ languageId: PainlessLang.ID,
+ suggestionProvider,
height: EDITOR_PX_HEIGHT.extraSmall,
options: {
lineNumbers: 'off',
diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx
index de28f6676660..8685738b3927 100644
--- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx
+++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_processors_editor/components/processor_form/processors/script.tsx
@@ -4,12 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useState } from 'react';
+import React, { useState, useEffect } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
+import { PainlessLang } from '@kbn/monaco';
import { EuiCode, EuiSwitch, EuiFormRow } from '@elastic/eui';
-import { FIELD_TYPES, fieldValidators, UseField, Field } from '../../../../../../shared_imports';
+import {
+ FIELD_TYPES,
+ fieldValidators,
+ UseField,
+ Field,
+ useFormData,
+} from '../../../../../../shared_imports';
import { XJsonEditor, TextEditor } from '../field_components';
@@ -122,6 +129,17 @@ const fieldsConfig: FieldsConfig = {
export const Script: FormFieldsComponent = ({ initialFieldValues }) => {
const [showId, setShowId] = useState(() => !!initialFieldValues?.id);
+ const [scriptLanguage, setScriptLanguage] = useState('plaintext');
+
+ const [{ fields }] = useFormData({ watch: 'fields.lang' });
+
+ const suggestionProvider = PainlessLang.getSuggestionProvider('processor_conditional');
+
+ useEffect(() => {
+ const isPainlessLang = fields?.lang === 'painless' || fields?.lang === ''; // Scripting language defaults to painless if none specified
+ setScriptLanguage(isPainlessLang ? PainlessLang.ID : 'plaintext');
+ }, [fields]);
+
return (
<>
@@ -147,6 +165,9 @@ export const Script: FormFieldsComponent = ({ initialFieldValues }) => {
component={TextEditor}
componentProps={{
editorProps: {
+ languageId: scriptLanguage,
+ suggestionProvider:
+ scriptLanguage === PainlessLang.ID ? suggestionProvider : undefined,
height: EDITOR_PX_HEIGHT.medium,
'aria-label': i18n.translate(
'xpack.ingestPipelines.pipelineEditor.scriptForm.sourceFieldAriaLabel',
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
index 4231f4b53997..f372c0c25b43 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx
@@ -247,8 +247,7 @@ export function LayerPanel(
const isFromTheSameGroup =
isDraggedOperation(dragging) &&
dragging.groupId === group.groupId &&
- dragging.columnId !== accessor &&
- dragging.groupId !== 'y'; // TODO: remove this line when https://github.com/elastic/elastic-charts/issues/868 is fixed
+ dragging.columnId !== accessor;
const isDroppable = isDraggedOperation(dragging)
? dragType === 'reorder'
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
index dbfffb5c2bd5..450918f1d13f 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
@@ -1229,6 +1229,24 @@ describe('IndexPatternDimensionEditorPanel', () => {
);
});
+ it('should not update when selecting the current field again', () => {
+ wrapper = mount();
+
+ const comboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="indexPattern-dimension-field"]');
+
+ const option = comboBox
+ .prop('options')![1]
+ .options!.find(({ label }) => label === 'timestampLabel')!;
+
+ act(() => {
+ comboBox.prop('onChange')!([option]);
+ });
+
+ expect(setState).not.toHaveBeenCalled();
+ });
+
it('should show all operations that are not filtered out', () => {
wrapper = mount(
{
return (
diff --git a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts
index e5764eaf0e8c..210101dc25c7 100644
--- a/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts
+++ b/x-pack/plugins/lens/public/xy_visualization/color_assignment.ts
@@ -24,7 +24,7 @@ export type ColorAssignments = Record<
string,
{
totalSeriesCount: number;
- getRank(layer: LayerColorConfig, seriesKey: string, yAccessor: string): number;
+ getRank(sortedLayer: LayerColorConfig, seriesKey: string, yAccessor: string): number;
}
>;
@@ -72,8 +72,8 @@ export function getColorAssignments(
);
return {
totalSeriesCount,
- getRank(layer: LayerColorConfig, seriesKey: string, yAccessor: string) {
- const layerIndex = paletteLayers.indexOf(layer);
+ getRank(sortedLayer: LayerColorConfig, seriesKey: string, yAccessor: string) {
+ const layerIndex = paletteLayers.findIndex((l) => sortedLayer.layerId === l.layerId);
const currentSeriesPerLayer = seriesPerLayer[layerIndex];
const splitRank = currentSeriesPerLayer.splits.indexOf(seriesKey);
return (
@@ -82,8 +82,10 @@ export function getColorAssignments(
: seriesPerLayer
.slice(0, layerIndex)
.reduce((sum, perLayer) => sum + perLayer.numberOfSeries, 0)) +
- (layer.splitAccessor && splitRank !== -1 ? splitRank * layer.accessors.length : 0) +
- layer.accessors.indexOf(yAccessor)
+ (sortedLayer.splitAccessor && splitRank !== -1
+ ? splitRank * sortedLayer.accessors.length
+ : 0) +
+ sortedLayer.accessors.indexOf(yAccessor)
);
},
};
@@ -94,13 +96,12 @@ export function getAccessorColorConfig(
colorAssignments: ColorAssignments,
frame: FramePublicAPI,
layer: LayerConfig,
- sortedAccessors: string[],
paletteService: PaletteRegistry
): AccessorConfig[] {
const layerContainsSplits = Boolean(layer.splitAccessor);
const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' };
const totalSeriesCount = colorAssignments[currentPalette.name].totalSeriesCount;
- return sortedAccessors.map((accessor) => {
+ return layer.accessors.map((accessor) => {
const currentYConfig = layer.yConfig?.find((yConfig) => yConfig.forAccessor === accessor);
if (layerContainsSplits) {
return {
diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts
index d780ce85bad6..cab1a0185333 100644
--- a/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts
+++ b/x-pack/plugins/lens/public/xy_visualization/visualization.test.ts
@@ -558,6 +558,30 @@ describe('xy_visualization', () => {
const accessorConfig = breakdownConfig!.accessors[0];
expect(typeof accessorConfig !== 'string' && accessorConfig.palette).toEqual(customColors);
});
+
+ it('should respect the order of accessors coming from datasource', () => {
+ mockDatasource.publicAPIMock.getTableSpec.mockReturnValue([
+ { columnId: 'c' },
+ { columnId: 'b' },
+ ]);
+ const paletteGetter = jest.spyOn(paletteServiceMock, 'get');
+ // overrite palette with a palette returning first blue, then green as color
+ paletteGetter.mockReturnValue({
+ id: 'default',
+ title: '',
+ getColors: jest.fn(),
+ toExpression: jest.fn(),
+ getColor: jest.fn().mockReturnValueOnce('blue').mockReturnValueOnce('green'),
+ });
+
+ const yConfigs = callConfigForYConfigs({});
+ expect(yConfigs?.accessors[0].columnId).toEqual('c');
+ expect(yConfigs?.accessors[0].color).toEqual('blue');
+ expect(yConfigs?.accessors[1].columnId).toEqual('b');
+ expect(yConfigs?.accessors[1].color).toEqual('green');
+
+ paletteGetter.mockClear();
+ });
});
});
diff --git a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx
index ebf80c61e0cd..e05871fd35a5 100644
--- a/x-pack/plugins/lens/public/xy_visualization/visualization.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/visualization.tsx
@@ -187,8 +187,10 @@ export const getXyVisualization = ({
mappedAccessors = getAccessorColorConfig(
colorAssignments,
frame,
- layer,
- sortedAccessors,
+ {
+ ...layer,
+ accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)),
+ },
paletteService
);
}
diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx
index cd8a5993d3ec..dc6ce285754f 100644
--- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx
@@ -51,6 +51,7 @@ import { TooltipWrapper } from './tooltip_wrapper';
import { getAxesConfiguration } from './axes_configuration';
import { PalettePicker } from '../shared_components';
import { getAccessorColorConfig, getColorAssignments } from './color_assignment';
+import { getSortedAccessors } from './to_expression';
type UnwrapArray = T extends Array ? P : T;
type AxesSettingsConfigKeys = keyof AxesSettingsConfig;
@@ -579,6 +580,9 @@ const ColorPicker = ({
const currentColor = useMemo(() => {
if (overwriteColor || !frame.activeData) return overwriteColor;
+ const datasource = frame.datasourceLayers[layer.layerId];
+ const sortedAccessors: string[] = getSortedAccessors(datasource, layer);
+
const colorAssignments = getColorAssignments(
state.layers,
{ tables: frame.activeData },
@@ -587,11 +591,14 @@ const ColorPicker = ({
const mappedAccessors = getAccessorColorConfig(
colorAssignments,
frame,
- layer,
- [accessor],
+ {
+ ...layer,
+ accessors: sortedAccessors.filter((sorted) => layer.accessors.includes(sorted)),
+ },
paletteService
);
- return mappedAccessors[0].color;
+
+ return mappedAccessors.find((a) => a.columnId === accessor)?.color || null;
}, [overwriteColor, frame, paletteService, state.layers, accessor, formatFactory, layer]);
const [color, setColor] = useState(currentColor);
diff --git a/x-pack/test/api_integration/apis/metrics_ui/metadata.ts b/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
index 349b0dcbd9cf..e319e59045d2 100644
--- a/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
+++ b/x-pack/test/api_integration/apis/metrics_ui/metadata.ts
@@ -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',
diff --git a/x-pack/test/functional/apps/lens/drag_and_drop.ts b/x-pack/test/functional/apps/lens/drag_and_drop.ts
index e0130bc39427..b85f36f9f525 100644
--- a/x-pack/test/functional/apps/lens/drag_and_drop.ts
+++ b/x-pack/test/functional/apps/lens/drag_and_drop.ts
@@ -60,7 +60,7 @@ export default function ({ getPageObjects }: FtrProviderContext) {
]);
});
- it('should move the column to compatible dimension group', async () => {
+ it.skip('should move the column to compatible dimension group', async () => {
await PageObjects.lens.switchToVisualization('bar');
expect(await PageObjects.lens.getDimensionTriggersTexts('lnsXY_xDimensionPanel')).to.eql([
'Top values of @message.raw',