[8.x] [Infra][ECO] Adding summary API (#194612) (#194916)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Infra][ECO] Adding summary API
(#194612)](https://github.com/elastic/kibana/pull/194612)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Cauê
Marcondes","email":"55978943+cauemarcondes@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-04T08:46:54Z","message":"[Infra][ECO]
Adding summary API (#194612)\n\ncloses
https://github.com/elastic/kibana/issues/193701\r\n\r\nThis PR does a
few things:\r\n- Adds a new API endpoint on infra to fetch an entity
summary:\r\n`/api/infra/entities/{entityType}/{entityId}/summary`. It
fetches the\r\nlatest EEM index filtering by entity.type(host |
container) and\r\ndepending on the entity type host.name or
container.id. And it returns\r\nthe following payload:\r\n```\r\n{\r\n
\"sourceDataStreams\": [\r\n \"logs\",\r\n \"metrics\"\r\n ],\r\n
\"entityId\": \"caues-mbp\",\r\n \"entityType\":
\"host\"\r\n}\r\n```\r\n- Fix a problem on the `Service` entity
definition removing the\r\n`datastream.type` and moving it to
`source_data_stream.type` due to ECS\r\nconflicts.\r\n- Moves some
common field definitions to **observability-shared**\r\nplugin, and
updated APM and Inventory
plugin.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a91d00731ae732c3bb2d370651048d94dd8adc2b","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","ci:project-deploy-observability","Team:obs-ux-infra_services","v8.16.0"],"title":"[Infra][ECO]
Adding summary
API","number":194612,"url":"https://github.com/elastic/kibana/pull/194612","mergeCommit":{"message":"[Infra][ECO]
Adding summary API (#194612)\n\ncloses
https://github.com/elastic/kibana/issues/193701\r\n\r\nThis PR does a
few things:\r\n- Adds a new API endpoint on infra to fetch an entity
summary:\r\n`/api/infra/entities/{entityType}/{entityId}/summary`. It
fetches the\r\nlatest EEM index filtering by entity.type(host |
container) and\r\ndepending on the entity type host.name or
container.id. And it returns\r\nthe following payload:\r\n```\r\n{\r\n
\"sourceDataStreams\": [\r\n \"logs\",\r\n \"metrics\"\r\n ],\r\n
\"entityId\": \"caues-mbp\",\r\n \"entityType\":
\"host\"\r\n}\r\n```\r\n- Fix a problem on the `Service` entity
definition removing the\r\n`datastream.type` and moving it to
`source_data_stream.type` due to ECS\r\nconflicts.\r\n- Moves some
common field definitions to **observability-shared**\r\nplugin, and
updated APM and Inventory
plugin.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a91d00731ae732c3bb2d370651048d94dd8adc2b"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194612","number":194612,"mergeCommit":{"message":"[Infra][ECO]
Adding summary API (#194612)\n\ncloses
https://github.com/elastic/kibana/issues/193701\r\n\r\nThis PR does a
few things:\r\n- Adds a new API endpoint on infra to fetch an entity
summary:\r\n`/api/infra/entities/{entityType}/{entityId}/summary`. It
fetches the\r\nlatest EEM index filtering by entity.type(host |
container) and\r\ndepending on the entity type host.name or
container.id. And it returns\r\nthe following payload:\r\n```\r\n{\r\n
\"sourceDataStreams\": [\r\n \"logs\",\r\n \"metrics\"\r\n ],\r\n
\"entityId\": \"caues-mbp\",\r\n \"entityType\":
\"host\"\r\n}\r\n```\r\n- Fix a problem on the `Service` entity
definition removing the\r\n`datastream.type` and moving it to
`source_data_stream.type` due to ECS\r\nconflicts.\r\n- Moves some
common field definitions to **observability-shared**\r\nplugin, and
updated APM and Inventory
plugin.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a91d00731ae732c3bb2d370651048d94dd8adc2b"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-10-04 20:28:20 +10:00 committed by GitHub
parent 7e77855c4e
commit d6f5f34dff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 250 additions and 83 deletions

View file

@ -20,7 +20,7 @@ const serviceTransactionFilter = (additionalFilters: string[] = []) => {
export const builtInServicesFromLogsEntityDefinition: EntityDefinition =
entityDefinitionSchema.parse({
version: '0.2.0',
version: '0.3.0',
id: `${BUILT_IN_ID_PREFIX}services_from_ecs_data`,
name: 'Services from ECS data',
description:
@ -46,8 +46,15 @@ export const builtInServicesFromLogsEntityDefinition: EntityDefinition =
displayNameTemplate: '{{service.name}}',
metadata: [
{ source: '_index', destination: 'sourceIndex' },
{
source: 'data_stream.type',
destination: 'source_data_stream.type',
},
{
source: 'data_stream.dataset',
destination: 'source_data_stream.dataset',
},
{ source: 'agent.name', aggregation: { type: 'terms', limit: 100 } },
'data_stream.type',
'service.environment',
'service.name',
'service.namespace',

View file

@ -31,6 +31,7 @@ export async function findEntityDefinitions({
page = 1,
perPage = 10,
includeState = false,
type,
}: {
soClient: SavedObjectsClientContract;
esClient: ElasticsearchClient;
@ -39,12 +40,14 @@ export async function findEntityDefinitions({
page?: number;
perPage?: number;
includeState?: boolean;
type?: string;
}): Promise<EntityDefinition[] | EntityDefinitionWithState[]> {
const filter = compact([
typeof builtIn === 'boolean'
? `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${BUILT_IN_ID_PREFIX}*)`
: undefined,
id ? `${SO_ENTITY_DEFINITION_TYPE}.attributes.id:(${id})` : undefined,
type ? `${SO_ENTITY_DEFINITION_TYPE}.attributes.type:(${type})` : undefined,
]).join(' AND ');
const response = await soClient.find<EntityDefinition>({
type: SO_ENTITY_DEFINITION_TYPE,

View file

@ -75,11 +75,15 @@ export class EntityClient {
page = 1,
perPage = 10,
includeState = false,
type,
builtIn,
}: {
id?: string;
page?: number;
perPage?: number;
includeState?: boolean;
type?: string;
builtIn?: boolean;
}) {
const definitions = await findEntityDefinitions({
esClient: this.options.esClient,
@ -88,6 +92,8 @@ export class EntityClient {
perPage,
id,
includeState,
type,
builtIn,
});
return { definitions };

View file

@ -5,13 +5,8 @@
* 2.0.
*/
export const ENTITY = 'entity';
export const LAST_SEEN = 'entity.lastSeenTimestamp';
export const FIRST_SEEN = 'entity.firstSeenTimestamp';
export const ENTITY_ID = 'entity.id';
export const ENTITY_METRICS_LATENCY = 'entity.metrics.latency';
export const ENTITY_METRICS_LOG_ERROR_RATE = 'entity.metrics.logErrorRate';
export const ENTITY_METRICS_LOG_RATE = 'entity.metrics.logRate';
export const ENTITY_METRICS_THROUGHPUT = 'entity.metrics.throughput';
export const ENTITY_METRICS_FAILED_TRANSACTION_RATE = 'entity.metrics.failedTransactionRate';
export const ENTITY_TYPE = 'entity.type';

View file

@ -5,7 +5,10 @@
* 2.0.
*/
import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { FIRST_SEEN, LAST_SEEN } from '../../../common/es_fields/entities';
import {
ENTITY_FIRST_SEEN,
ENTITY_LAST_SEEN,
} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
import type { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
import { getEntityLatestServices } from './get_entity_latest_services';
import type { EntityLatestServiceRaw } from './types';
@ -19,14 +22,14 @@ export function entitiesRangeQuery(start?: number, end?: number): QueryDslQueryC
return [
{
range: {
[LAST_SEEN]: {
[ENTITY_LAST_SEEN]: {
gte: start,
},
},
},
{
range: {
[FIRST_SEEN]: {
[ENTITY_FIRST_SEEN]: {
lte: end,
},
},

View file

@ -5,16 +5,18 @@
* 2.0.
*/
import { termsQuery, rangeQuery } from '@kbn/observability-plugin/server';
import { EntityMetrics } from '../../../common/entities/types';
import { rangeQuery, termsQuery } from '@kbn/observability-plugin/server';
import {
ENTITY_ID,
ENTITY_LAST_SEEN,
} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
import { EntityMetrics } from '../../../common/entities/types';
import {
ENTITY_METRICS_FAILED_TRANSACTION_RATE,
ENTITY_METRICS_LATENCY,
ENTITY_METRICS_LOG_ERROR_RATE,
ENTITY_METRICS_LOG_RATE,
ENTITY_METRICS_THROUGHPUT,
LAST_SEEN,
} from '../../../common/es_fields/entities';
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
@ -39,7 +41,10 @@ export async function getEntityHistoryServicesMetrics({
track_total_hits: false,
query: {
bool: {
filter: [...rangeQuery(start, end, LAST_SEEN), ...termsQuery(ENTITY_ID, ...entityIds)],
filter: [
...rangeQuery(start, end, ENTITY_LAST_SEEN),
...termsQuery(ENTITY_ID, ...entityIds),
],
},
},
aggs: {

View file

@ -5,20 +5,20 @@
* 2.0.
*/
import { termsQuery, rangeQuery } from '@kbn/observability-plugin/server';
import { getBucketSize } from '@kbn/apm-data-access-plugin/common';
import { rangeQuery, termsQuery } from '@kbn/observability-plugin/server';
import { ENTITY_LAST_SEEN } from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
import { keyBy } from 'lodash';
import { SERVICE_NAME } from '../../../common/es_fields/apm';
import {
ENTITY_METRICS_FAILED_TRANSACTION_RATE,
ENTITY_METRICS_LATENCY,
ENTITY_METRICS_LOG_ERROR_RATE,
ENTITY_METRICS_LOG_RATE,
ENTITY_METRICS_THROUGHPUT,
LAST_SEEN,
} from '../../../common/es_fields/entities';
import { SERVICE_NAME } from '../../../common/es_fields/apm';
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
import { environmentQuery } from '../../../common/utils/environment_query';
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
interface Params {
entitiesESClient: EntitiesESClient;
@ -48,7 +48,7 @@ export async function getEntityHistoryServicesTimeseries({
query: {
bool: {
filter: [
...rangeQuery(start, end, LAST_SEEN),
...rangeQuery(start, end, ENTITY_LAST_SEEN),
...termsQuery(SERVICE_NAME, ...serviceNames),
...environmentQuery(environment),
],

View file

@ -7,12 +7,11 @@
import { kqlQuery, termQuery } from '@kbn/observability-plugin/server';
import {
AGENT_NAME,
DATA_STEAM_TYPE,
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '../../../common/es_fields/apm';
import { ENTITY, ENTITY_TYPE } from '../../../common/es_fields/entities';
ENTITY,
ENTITY_TYPE,
SOURCE_DATA_STREAM_TYPE,
} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
import { AGENT_NAME, SERVICE_ENVIRONMENT, SERVICE_NAME } from '../../../common/es_fields/apm';
import { environmentQuery } from '../../../common/utils/environment_query';
import { EntitiesESClient } from '../../lib/helpers/create_es_client/create_entities_es_client/create_entities_es_client';
import { entitiesRangeQuery } from './get_entities';
@ -40,7 +39,7 @@ export async function getEntityLatestServices({
body: {
size,
track_total_hits: false,
_source: [AGENT_NAME, ENTITY, DATA_STEAM_TYPE, SERVICE_NAME, SERVICE_ENVIRONMENT],
_source: [AGENT_NAME, ENTITY, SOURCE_DATA_STREAM_TYPE, SERVICE_NAME, SERVICE_ENVIRONMENT],
query: {
bool: {
filter: [

View file

@ -15,7 +15,7 @@ export interface EntityLatestServiceRaw {
agent: {
name: AgentName[];
};
data_stream: {
source_data_stream: {
type: string[];
};
service: {

View file

@ -18,7 +18,7 @@ describe('mergeEntities', () => {
environment: 'test',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['metrics', 'logs'] },
source_data_stream: { type: ['metrics', 'logs'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -64,7 +64,7 @@ describe('mergeEntities', () => {
environment: 'env-service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['foo'] },
source_data_stream: { type: ['foo'] },
entity: {
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
@ -85,7 +85,7 @@ describe('mergeEntities', () => {
environment: 'env-service-2',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['bar'] },
source_data_stream: { type: ['bar'] },
entity: {
firstSeenTimestamp: '2024-03-05T10:34:40.810Z',
lastSeenTimestamp: '2024-03-05T10:34:40.810Z',
@ -106,7 +106,7 @@ describe('mergeEntities', () => {
environment: 'env-service-3',
},
agent: { name: ['java'] },
data_stream: { type: ['baz'] },
source_data_stream: { type: ['baz'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -127,7 +127,7 @@ describe('mergeEntities', () => {
environment: 'env-service-4',
},
agent: { name: ['java'] },
data_stream: { type: ['baz'] },
source_data_stream: { type: ['baz'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -204,7 +204,7 @@ describe('mergeEntities', () => {
environment: 'test',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['metrics', 'logs'] },
source_data_stream: { type: ['metrics', 'logs'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -225,7 +225,7 @@ describe('mergeEntities', () => {
environment: 'test',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['metrics', 'logs'] },
source_data_stream: { type: ['metrics', 'logs'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -246,7 +246,7 @@ describe('mergeEntities', () => {
environment: 'prod',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['foo'] },
source_data_stream: { type: ['foo'] },
entity: {
firstSeenTimestamp: '2024-23-05T10:34:40.810Z',
lastSeenTimestamp: '2024-23-05T10:34:40.810Z',
@ -305,7 +305,7 @@ describe('mergeEntities', () => {
environment: undefined,
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -348,7 +348,7 @@ describe('mergeEntities', () => {
name: 'service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -368,7 +368,7 @@ describe('mergeEntities', () => {
name: 'service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -420,7 +420,7 @@ describe('mergeEntities', () => {
name: 'service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -463,7 +463,7 @@ describe('mergeEntities', () => {
name: 'service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -483,7 +483,7 @@ describe('mergeEntities', () => {
name: 'service-1',
},
agent: { name: ['nodejs'] },
data_stream: { type: [] },
source_data_stream: { type: [] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',
@ -536,7 +536,7 @@ describe('mergeEntities', () => {
environment: 'test',
},
agent: { name: ['nodejs'] },
data_stream: { type: ['metrics'] },
source_data_stream: { type: ['metrics'] },
entity: {
firstSeenTimestamp: '2024-06-05T10:34:40.810Z',
lastSeenTimestamp: '2024-06-05T10:34:40.810Z',

View file

@ -53,7 +53,7 @@ function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServic
if (!existingEntity) {
return {
...commonEntityFields,
dataStreamTypes: entity.data_stream.type,
dataStreamTypes: entity.source_data_stream.type,
environments: compact([entity?.service.environment]),
metrics: [entity.entity.metrics],
hasLogMetrics,
@ -62,7 +62,7 @@ function mergeFunc(entity: EntityLatestServiceRaw, existingEntity?: MergedServic
return {
...commonEntityFields,
dataStreamTypes: uniq(
compact([...(existingEntity?.dataStreamTypes ?? []), ...entity.data_stream.type])
compact([...(existingEntity?.dataStreamTypes ?? []), ...entity.source_data_stream.type])
),
environments: uniq(compact([...existingEntity?.environments, entity?.service.environment])),
metrics: [...existingEntity?.metrics, entity.entity.metrics],

View file

@ -35,7 +35,8 @@
"usageCollection",
"visTypeTimeseries",
"apmDataAccess",
"logsDataAccess"
"logsDataAccess",
"entityManager"
],
"optionalPlugins": [
"spaces",

View file

@ -34,6 +34,7 @@ import { initProfilingRoutes } from './routes/profiling';
import { initServicesRoute } from './routes/services';
import { initCustomDashboardsRoutes } from './routes/custom_dashboards/custom_dashboards';
import { InfraBackendLibs } from './lib/infra_types';
import { initEntitiesConfigurationRoutes } from './routes/entities';
export const registerRoutes = (libs: InfraBackendLibs) => {
initIpToHostName(libs);
@ -63,4 +64,5 @@ export const registerRoutes = (libs: InfraBackendLibs) => {
initProfilingRoutes(libs);
initServicesRoute(libs);
initCustomDashboardsRoutes(libs.framework);
initEntitiesConfigurationRoutes(libs);
};

View file

@ -39,6 +39,10 @@ import {
ApmDataAccessPluginStart,
} from '@kbn/apm-data-access-plugin/server';
import { LogsDataAccessPluginStart } from '@kbn/logs-data-access-plugin/server';
import type {
EntityManagerServerPluginStart,
EntityManagerServerPluginSetup,
} from '@kbn/entityManager-plugin/server';
export interface InfraServerPluginSetupDeps {
alerting: AlertingPluginContract;
@ -56,6 +60,7 @@ export interface InfraServerPluginSetupDeps {
metricsDataAccess: MetricsDataPluginSetup;
profilingDataAccess?: ProfilingDataAccessPluginSetup;
apmDataAccess: ApmDataAccessPluginSetup;
entityManager: EntityManagerServerPluginSetup;
}
export interface InfraServerPluginStartDeps {
@ -66,6 +71,7 @@ export interface InfraServerPluginStartDeps {
ruleRegistry: RuleRegistryPluginStartContract;
apmDataAccess: ApmDataAccessPluginStart;
logsDataAccess: LogsDataAccessPluginStart;
entityManager: EntityManagerServerPluginStart;
}
export interface CallWithRequestParams extends estypes.RequestBase {

View file

@ -296,6 +296,7 @@ export class InfraServerPlugin
const coreContext = await context.core;
const savedObjectsClient = coreContext.savedObjects.client;
const uiSettingsClient = coreContext.uiSettings.client;
const entityManager = await this.libs.plugins.entityManager.start();
const mlSystem = plugins.ml?.mlSystemProvider(request, savedObjectsClient);
const mlAnomalyDetectors = plugins.ml?.anomalyDetectorsProvider(
@ -317,6 +318,7 @@ export class InfraServerPlugin
savedObjectsClient,
uiSettingsClient,
getMetricsIndices,
entityManager,
};
}
);

View file

@ -0,0 +1,50 @@
/*
* 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 { type ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
import { esqlResultToPlainObjects } from '@kbn/observability-utils/es/utils/esql_result_to_plain_objects';
import { ENTITY_LATEST, EntityDefinition, entitiesAliasPattern } from '@kbn/entities-schema';
import { type EntityDefinitionWithState } from '@kbn/entityManager-plugin/server/lib/entities/types';
import {
ENTITY_TYPE,
SOURCE_DATA_STREAM_TYPE,
} from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
const ENTITIES_LATEST_ALIAS = entitiesAliasPattern({
type: '*',
dataset: ENTITY_LATEST,
});
interface Entity {
[SOURCE_DATA_STREAM_TYPE]: string | string[];
}
export async function getLatestEntity({
inventoryEsClient,
entityId,
entityType,
entityDefinitions,
}: {
inventoryEsClient: ObservabilityElasticsearchClient;
entityType: 'host' | 'container';
entityId: string;
entityDefinitions: EntityDefinition[] | EntityDefinitionWithState[];
}) {
const hostOrContainerIdentityField = entityDefinitions[0]?.identityFields?.[0]?.field;
if (hostOrContainerIdentityField === undefined) {
return;
}
const latestEntitiesEsqlResponse = await inventoryEsClient.esql('get_latest_entities', {
query: `FROM ${ENTITIES_LATEST_ALIAS}
| WHERE ${ENTITY_TYPE} == "${entityType}"
| WHERE ${hostOrContainerIdentityField} == "${entityId}"
| KEEP ${SOURCE_DATA_STREAM_TYPE}
`,
});
return esqlResultToPlainObjects<Entity>(latestEntitiesEsqlResponse)[0];
}

View file

@ -0,0 +1,80 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { METRICS_APP_ID } from '@kbn/deeplinks-observability/constants';
import { SOURCE_DATA_STREAM_TYPE } from '@kbn/observability-shared-plugin/common/field_names/elasticsearch';
import { createObservabilityEsClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
import { InfraBackendLibs } from '../../lib/infra_types';
import { getLatestEntity } from './get_latest_entity';
export const initEntitiesConfigurationRoutes = (libs: InfraBackendLibs) => {
const { framework, logger } = libs;
framework.registerRoute(
{
method: 'get',
path: '/api/infra/entities/{entityType}/{entityId}/summary',
validate: {
params: schema.object({
entityType: schema.oneOf([schema.literal('host'), schema.literal('container')]),
entityId: schema.string(),
}),
},
options: {
access: 'internal',
},
},
async (requestContext, request, response) => {
const { entityId, entityType } = request.params;
const coreContext = await requestContext.core;
const infraContext = await requestContext.infra;
const entityManager = await infraContext.entityManager.getScopedClient({ request });
const client = createObservabilityEsClient({
client: coreContext.elasticsearch.client.asCurrentUser,
logger,
plugin: `@kbn/${METRICS_APP_ID}-plugin`,
});
try {
// Only fetch built in definitions
const { definitions } = await entityManager.getEntityDefinitions({
builtIn: true,
type: entityType,
});
if (definitions.length === 0) {
return response.ok({
body: { sourceDataStreams: [], entityId, entityType },
});
}
const entity = await getLatestEntity({
inventoryEsClient: client,
entityId,
entityType,
entityDefinitions: definitions,
});
return response.ok({
body: {
sourceDataStreams: [entity?.[SOURCE_DATA_STREAM_TYPE] || []].flat() as string[],
entityId,
entityType,
},
});
} catch (error) {
return response.customError({
statusCode: error.statusCode ?? 500,
body: {
message: error.message ?? 'An unexpected error occurred',
},
});
}
}
);
};

View file

@ -13,6 +13,7 @@ import type {
} from '@kbn/core/server';
import type { SearchRequestHandlerContext } from '@kbn/data-plugin/server';
import type { MlPluginSetup } from '@kbn/ml-plugin/server';
import type { EntityManagerServerPluginStart } from '@kbn/entityManager-plugin/server';
import { InfraServerPluginStartDeps } from './lib/adapters/framework';
import { InventoryViewsServiceSetup, InventoryViewsServiceStart } from './services/inventory_views';
import {
@ -45,6 +46,7 @@ export interface InfraRequestHandlerContext {
savedObjectsClient: SavedObjectsClientContract;
uiSettingsClient: IUiSettingsClient;
getMetricsIndices: () => Promise<string>;
entityManager: EntityManagerServerPluginStart;
}
/**

View file

@ -109,7 +109,10 @@
"@kbn/core-analytics-browser",
"@kbn/observability-alerting-rule-utils",
"@kbn/core-application-browser",
"@kbn/shared-ux-page-no-data-types"
"@kbn/shared-ux-page-no-data-types",
"@kbn/entityManager-plugin",
"@kbn/observability-utils",
"@kbn/entities-schema"
],
"exclude": ["target/**/*"]
}

View file

@ -4,24 +4,22 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import * as t from 'io-ts';
import { ENTITY_LATEST, entitiesAliasPattern } from '@kbn/entities-schema';
import { isRight } from 'fp-ts/lib/Either';
import {
SERVICE_ENVIRONMENT,
SERVICE_NAME,
CONTAINER_ID,
HOST_NAME,
AGENT_NAME,
CLOUD_PROVIDER,
} from '@kbn/observability-shared-plugin/common';
import {
ENTITY_DEFINITION_ID,
ENTITY_DISPLAY_NAME,
ENTITY_ID,
ENTITY_LAST_SEEN,
ENTITY_TYPE,
} from './es_fields/entities';
SERVICE_ENVIRONMENT,
SERVICE_NAME,
} from '@kbn/observability-shared-plugin/common';
import { isRight } from 'fp-ts/lib/Either';
import * as t from 'io-ts';
export const entityTypeRt = t.union([
t.literal('service'),

View file

@ -1,12 +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.
*/
export const ENTITY_LAST_SEEN = 'entity.lastSeenTimestamp';
export const ENTITY_ID = 'entity.id';
export const ENTITY_TYPE = 'entity.type';
export const ENTITY_DISPLAY_NAME = 'entity.displayName';
export const ENTITY_DEFINITION_ID = 'entity.definitionId';

View file

@ -9,7 +9,7 @@ import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { BadgeFilterWithPopover } from '.';
import { EuiThemeProvider, copyToClipboard } from '@elastic/eui';
import { ENTITY_TYPE } from '../../../common/es_fields/entities';
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),

View file

@ -9,9 +9,9 @@ import { EuiDataGridSorting, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic
import { Meta, Story } from '@storybook/react';
import { orderBy } from 'lodash';
import React, { useMemo, useState } from 'react';
import { ENTITY_LAST_SEEN, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
import { EntitiesGrid } from '.';
import { EntityType } from '../../../common/entities';
import { ENTITY_LAST_SEEN, ENTITY_TYPE } from '../../../common/es_fields/entities';
import { entitiesMock } from './mock/entities_mock';
const stories: Meta<{}> = {

View file

@ -11,10 +11,11 @@ import {
AssetDetailsLocatorParams,
ASSET_DETAILS_LOCATOR_ID,
ServiceOverviewParams,
ENTITY_TYPE,
ENTITY_DISPLAY_NAME,
} from '@kbn/observability-shared-plugin/common';
import { useKibana } from '../../../hooks/use_kibana';
import { EntityIcon } from '../../entity_icon';
import { ENTITY_DISPLAY_NAME, ENTITY_TYPE } from '../../../../common/es_fields/entities';
import { Entity } from '../../../../common/entities';
import { parseServiceParams } from '../../../utils/parse_service_params';

View file

@ -18,16 +18,16 @@ import { i18n } from '@kbn/i18n';
import { FormattedDate, FormattedMessage, FormattedTime } from '@kbn/i18n-react';
import { last } from 'lodash';
import React, { useCallback, useState } from 'react';
import type { EntityType } from '../../../common/entities';
import {
ENTITY_DISPLAY_NAME,
ENTITY_LAST_SEEN,
ENTITY_TYPE,
} from '../../../common/es_fields/entities';
import type { APIReturnType } from '../../api';
import { getEntityTypeLabel } from '../../utils/get_entity_type_label';
} from '@kbn/observability-shared-plugin/common';
import { APIReturnType } from '../../api';
import { BadgeFilterWithPopover } from '../badge_filter_with_popover';
import { EntityName } from './entity_name';
import { EntityType } from '../../../common/entities';
import { getEntityTypeLabel } from '../../utils/get_entity_type_label';
type InventoryEntitiesAPIReturnType = APIReturnType<'GET /internal/inventory/entities'>;
type LatestEntities = InventoryEntitiesAPIReturnType['entities'];
@ -147,7 +147,7 @@ export function EntitiesGrid({
const columnEntityTableId = columnId as EntityColumnIds;
switch (columnEntityTableId) {
case ENTITY_TYPE:
const entityType = entity[columnEntityTableId] as EntityType;
const entityType = entity[columnEntityTableId];
return (
<BadgeFilterWithPopover
field={ENTITY_TYPE}

View file

@ -6,12 +6,11 @@
*/
import React from 'react';
import { AGENT_NAME, CLOUD_PROVIDER } from '@kbn/observability-shared-plugin/common';
import { AGENT_NAME, CLOUD_PROVIDER, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
import { type CloudProvider, CloudProviderIcon, AgentIcon } from '@kbn/custom-icons';
import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
import type { AgentName } from '@kbn/elastic-agent-utils';
import { euiThemeVars } from '@kbn/ui-theme';
import { ENTITY_TYPE } from '../../../common/es_fields/entities';
import type { Entity } from '../../../common/entities';
interface EntityIconProps {

View file

@ -5,22 +5,22 @@
* 2.0.
*/
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButton } from '@elastic/eui';
import { DataView } from '@kbn/data-views-plugin/public';
import { buildPhrasesFilter, PhrasesFilter } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import React, { useMemo } from 'react';
import { useKibana } from '../../hooks/use_kibana';
import {
ENTITY_DEFINITION_ID,
ENTITY_DISPLAY_NAME,
ENTITY_LAST_SEEN,
ENTITY_TYPE,
} from '../../../common/es_fields/entities';
import { EntityColumnIds } from '../entities_grid';
} from '@kbn/observability-shared-plugin/common';
import { defaultEntityDefinitions } from '../../../common/entities';
import { useInventoryParams } from '../../hooks/use_inventory_params';
import { useKibana } from '../../hooks/use_kibana';
import { EntityColumnIds } from '../entities_grid';
const ACTIVE_COLUMNS: EntityColumnIds[] = [ENTITY_DISPLAY_NAME, ENTITY_TYPE, ENTITY_LAST_SEEN];

View file

@ -8,7 +8,7 @@ import { toNumberRt } from '@kbn/io-ts-utils';
import { Outlet, createRouter } from '@kbn/typed-react-router-config';
import * as t from 'io-ts';
import React from 'react';
import { ENTITY_LAST_SEEN } from '../../common/es_fields/entities';
import { ENTITY_LAST_SEEN } from '@kbn/observability-shared-plugin/common';
import { InventoryPageTemplate } from '../components/inventory_page_template';
import { InventoryPage } from '../pages/inventory_page';
import { entityTypesRt } from '../../common/entities';

View file

@ -6,8 +6,8 @@
*/
import { type ObservabilityElasticsearchClient } from '@kbn/observability-utils/es/client/create_observability_es_client';
import { ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
import { ENTITIES_LATEST_ALIAS, EntityType } from '../../../common/entities';
import { ENTITY_TYPE } from '../../../common/es_fields/entities';
import { getEntityDefinitionIdWhereClause, getEntityTypesWhereClause } from './query_helper';
export async function getEntityTypes({

View file

@ -5,8 +5,8 @@
* 2.0.
*/
import { ENTITY_DEFINITION_ID, ENTITY_TYPE } from '@kbn/observability-shared-plugin/common';
import { EntityType, defaultEntityTypes, defaultEntityDefinitions } from '../../../common/entities';
import { ENTITY_DEFINITION_ID, ENTITY_TYPE } from '../../../common/es_fields/entities';
export const getEntityTypesWhereClause = (entityTypes: EntityType[] = defaultEntityTypes) =>
`WHERE ${ENTITY_TYPE} IN (${entityTypes.map((entityType) => `"${entityType}"`).join()})`;

View file

@ -145,3 +145,12 @@ export const PROFILE_ALLOC_OBJECTS = 'profile.alloc_objects.count';
export const PROFILE_ALLOC_SPACE = 'profile.alloc_space.bytes';
export const PROFILE_INUSE_OBJECTS = 'profile.inuse_objects.count';
export const PROFILE_INUSE_SPACE = 'profile.inuse_space.bytes';
export const ENTITY = 'entity';
export const ENTITY_ID = 'entity.id';
export const ENTITY_TYPE = 'entity.type';
export const ENTITY_LAST_SEEN = 'entity.lastSeenTimestamp';
export const ENTITY_FIRST_SEEN = 'entity.firstSeenTimestamp';
export const ENTITY_DISPLAY_NAME = 'entity.displayName';
export const ENTITY_DEFINITION_ID = 'entity.definitionId';
export const SOURCE_DATA_STREAM_TYPE = 'source_data_stream.type';

View file

@ -128,6 +128,14 @@ export {
PROFILE_ALLOC_SPACE,
PROFILE_INUSE_OBJECTS,
PROFILE_INUSE_SPACE,
ENTITY,
ENTITY_DEFINITION_ID,
ENTITY_DISPLAY_NAME,
ENTITY_FIRST_SEEN,
ENTITY_ID,
ENTITY_LAST_SEEN,
ENTITY_TYPE,
SOURCE_DATA_STREAM_TYPE,
} from './field_names/elasticsearch';
export {