Include additional fields for summary column (#213687)

Resolves #196513

## Summary

Updates fields in Discover summary column.

## Logic

remove

```
cloud.instance.id 
orchestrator.namespace
```

In addition we need to implement some logic to have a fallback,
selecting the first field with a value in descending order:

Add fallback mechanism to container.name
```
kubernetes.container.name => k8s.container.name => container.name 
```

Add fallback mechanism to host.name
```
kubernetes.node.name => k8s.node.name => host.name
```
  
Add fallback mechanism to cluster.name
```
orchestrator.cluster.name => k8s.cluster.name
```

Add new fields with fallbacks

```
kubernetes.namespace => (k8s.namespace.name)
kubernetes.pod.name => (k8s.pod.name)

// only one of these will be present in a single doc:
kubernetes.deployment.name => (k8s.deployment.name)
kubernetes.replicaset.name => (k8s.replicaset.name)
kubernetes.statefulset.name => (k8s.statefulset.name)
kubernetes.daemonset.name => (k8s.daemonset.name)
kubernetes.job.name => (k8s.job.name)
kubernetes.cronjob.name => (k8s.cronjob.name)
```
This commit is contained in:
Thom Heymann 2025-03-13 11:25:56 +00:00 committed by GitHub
parent 686a4cb64f
commit 37c88f9618
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 131 additions and 24 deletions

View file

@ -130,8 +130,7 @@ describe('SummaryColumn', () => {
it('should render a badge indicating the count of additional resources', () => {
const record = getBaseRecord();
renderSummary(record);
expect(screen.queryByText('+2')).toBeInTheDocument();
expect(screen.queryByText('+3')).not.toBeInTheDocument();
expect(screen.queryByText('+1')).toBeInTheDocument();
});
it('should render all the available resources in row autofit/custom mode', () => {
@ -149,14 +148,6 @@ describe('SummaryColumn', () => {
expect(
screen.queryByTestId(`dataTableCellActionsPopover_${constants.HOST_NAME_FIELD}`)
).toBeInTheDocument();
expect(
screen.queryByTestId(
`dataTableCellActionsPopover_${constants.ORCHESTRATOR_NAMESPACE_FIELD}`
)
).toBeInTheDocument();
expect(
screen.queryByTestId(`dataTableCellActionsPopover_${constants.CLOUD_INSTANCE_ID_FIELD}`)
).toBeInTheDocument();
expect(screen.queryByText('+2')).not.toBeInTheDocument();
});

View file

@ -84,8 +84,6 @@ export const RESOURCE_FIELDS = [
fieldConstants.SERVICE_NAME_FIELD,
fieldConstants.CONTAINER_NAME_FIELD,
fieldConstants.HOST_NAME_FIELD,
fieldConstants.ORCHESTRATOR_NAMESPACE_FIELD,
fieldConstants.CLOUD_INSTANCE_ID_FIELD,
fieldConstants.ORCHESTRATOR_RESOURCE_ID_FIELD,
fieldConstants.ORCHESTRATOR_CLUSTER_NAME_FIELD,
fieldConstants.ORCHESTRATOR_CLUSTER_ID_FIELD,

View file

@ -68,14 +68,35 @@ export interface LogFlyoutDoc {
}
export interface ResourceFields {
'host.name'?: string;
'service.name'?: string;
'agent.name'?: string;
'kubernetes.container.name'?: string;
'k8s.container.name'?: string;
'container.name'?: string;
'kubernetes.node.name'?: string;
'k8s.node.name'?: string;
'host.name'?: string;
'orchestrator.cluster.name'?: string;
'k8s.cluster.name'?: string;
'kubernetes.namespace'?: string;
'k8s.namespace.name'?: string;
'kubernetes.pod.name'?: string;
'k8s.pod.name'?: string;
'kubernetes.deployment.name'?: string;
'k8s.deployment.name'?: string;
'kubernetes.replicaset.name'?: string;
'k8s.replicaset.name'?: string;
'kubernetes.statefulset.name'?: string;
'k8s.statefulset.name'?: string;
'kubernetes.daemonset.name'?: string;
'k8s.daemonset.name'?: string;
'kubernetes.job.name'?: string;
'k8s.job.name'?: string;
'kubernetes.cronjob.name'?: string;
'k8s.cronjob.name'?: string;
'agent.name'?: string;
'orchestrator.cluster.id'?: string;
'orchestrator.resource.id'?: string;
'orchestrator.namespace'?: string;
'container.name'?: string;
'container.id'?: string;
'cloud.instance.id'?: string;
}

View file

@ -0,0 +1,75 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { getAvailableResourceFields } from './get_available_resource_fields';
import type { ResourceFields } from '../../..';
describe('getAvailableResourceFields', () => {
it('should return available fields', () => {
const fields = getAvailableResourceFields({
'service.name': 'yes',
'container.name': 'yes',
'host.name': 'yes',
'k8s.cluster.name': 'yes',
'k8s.namespace.name': 'yes',
'k8s.pod.name': 'yes',
'k8s.cronjob.name': 'yes',
});
expect(fields).toEqual([
'service.name',
'container.name',
'host.name',
'k8s.cluster.name',
'k8s.namespace.name',
'k8s.pod.name',
'k8s.cronjob.name',
]);
});
it('should ignore empty fields', () => {
const fields = getAvailableResourceFields({
'service.name': '',
});
expect(fields).toEqual([]);
});
it('should ignore unknown fields', () => {
const fields = getAvailableResourceFields({
unknown: 'no',
} as ResourceFields);
expect(fields).toEqual([]);
});
it('should return first available field from each group', () => {
const fields = getAvailableResourceFields({
'service.name': 'yes',
'container.name': 'no',
'kubernetes.container.name': 'yes', // higher priority than `container.name`
'host.name': 'no',
'kubernetes.node.name': 'yes', // higher priority than `host.name`
'k8s.cluster.name': 'no',
'orchestrator.cluster.name': 'yes', // higher priority than `k8s.cluster.name`
'k8s.namespace.name': 'no',
'kubernetes.namespace': 'yes', // higher priority than `k8s.namespace.name`
'k8s.pod.name': 'no',
'kubernetes.pod.name': 'yes', // higher priority than `k8s.pod.name`
'k8s.cronjob.name': 'no',
'kubernetes.deployment.name': 'yes', // higher priority than `k8s.cronjob.name`
});
expect(fields).toEqual([
'service.name',
'kubernetes.container.name',
'kubernetes.node.name',
'orchestrator.cluster.name',
'kubernetes.namespace',
'kubernetes.pod.name',
'kubernetes.deployment.name',
]);
});
});

View file

@ -7,16 +7,38 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { ResourceFields } from '../../..';
import * as constants from '../constants';
import type { ResourceFields } from '../../..';
const AVAILABLE_RESOURCE_FIELDS: Array<keyof ResourceFields> = [
constants.SERVICE_NAME_FIELD,
constants.CONTAINER_NAME_FIELD,
constants.HOST_NAME_FIELD,
constants.ORCHESTRATOR_NAMESPACE_FIELD,
constants.CLOUD_INSTANCE_ID_FIELD,
// Use first available field from each group
const AVAILABLE_RESOURCE_FIELDS: Array<Array<keyof ResourceFields>> = [
['service.name'],
['kubernetes.container.name', 'k8s.container.name', 'container.name'],
['kubernetes.node.name', 'k8s.node.name', 'host.name'],
['orchestrator.cluster.name', 'k8s.cluster.name'],
['kubernetes.namespace', 'k8s.namespace.name'],
['kubernetes.pod.name', 'k8s.pod.name'],
// Only one of these will be present in a single doc
[
'kubernetes.deployment.name',
'k8s.deployment.name',
'kubernetes.replicaset.name',
'k8s.replicaset.name',
'kubernetes.statefulset.name',
'k8s.statefulset.name',
'kubernetes.daemonset.name',
'k8s.daemonset.name',
'kubernetes.job.name',
'k8s.job.name',
'kubernetes.cronjob.name',
'k8s.cronjob.name',
],
];
export const getAvailableResourceFields = (resourceDoc: ResourceFields) =>
AVAILABLE_RESOURCE_FIELDS.filter((fieldName) => Boolean(resourceDoc[fieldName]));
AVAILABLE_RESOURCE_FIELDS.reduce((acc, fields) => {
const field = fields.find((fieldName) => resourceDoc[fieldName]);
if (field) {
acc.push(field);
}
return acc;
}, []);