From da34b1afd2d92b7790ee1dcaafbeb15631b7ab9a Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 24 Sep 2024 17:07:41 +0200 Subject: [PATCH 1/5] [Fleet] POC - Add support for OTEL policies and integrations --- .../fleet/common/constants/package_policy.ts | 2 + .../plugins/fleet/common/constants/routes.ts | 8 +++ .../fleet/server/routes/otel/handlers.ts | 63 +++++++++++++++++ .../plugins/fleet/server/routes/otel/index.ts | 32 +++++++++ .../fleet/server/saved_objects/index.ts | 32 +++++++++ .../agent_policies/full_agent_policy.ts | 2 +- .../fleet/server/types/rest_spec/otel.ts | 70 +++++++++++++++++++ .../fleet/server/types/so_attributes.ts | 19 +++++ 8 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/fleet/server/routes/otel/handlers.ts create mode 100644 x-pack/plugins/fleet/server/routes/otel/index.ts create mode 100644 x-pack/plugins/fleet/server/types/rest_spec/otel.ts diff --git a/x-pack/plugins/fleet/common/constants/package_policy.ts b/x-pack/plugins/fleet/common/constants/package_policy.ts index 9ff84c65ad22b..82fca834027fd 100644 --- a/x-pack/plugins/fleet/common/constants/package_policy.ts +++ b/x-pack/plugins/fleet/common/constants/package_policy.ts @@ -17,3 +17,5 @@ export const inputsFormat = { } as const; export const LICENCE_FOR_MULTIPLE_AGENT_POLICIES = 'enterprise'; + +export const OTEL_POLICY_SAVED_OBJECT_TYPE = 'fleet-otel-policies'; diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 9b5c35c3b3ce2..892e063a7dc33 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -16,6 +16,7 @@ export const PACKAGE_POLICY_API_ROOT = `${API_ROOT}/package_policies`; export const AGENT_POLICY_API_ROOT = `${API_ROOT}/agent_policies`; export const K8S_API_ROOT = `${API_ROOT}/kubernetes`; export const DOWNLOAD_SOURCE_API_ROOT = `${API_ROOT}/agent_download_sources`; +export const OTEL_API_ROOT = `${API_ROOT}/otel`; export const LIMITED_CONCURRENCY_ROUTE_TAG = 'ingest:limited-concurrency'; @@ -215,6 +216,13 @@ export const DOWNLOAD_SOURCE_API_ROUTES = { DELETE_PATTERN: `${API_ROOT}/agent_download_sources/{sourceId}`, }; +// OTEL policies and templates routes +export const OTEL_INTEGRATIONS_ROUTES = { + CREATE_PATTERN: `${OTEL_API_ROOT}/templates/{pkgName}`, +}; +export const OTEL_POLICIES_ROUTES = { + CREATE_PATTERN: `${OTEL_API_ROOT}/policies`, +}; export const CREATE_STANDALONE_AGENT_API_KEY_ROUTE = `${INTERNAL_ROOT}/create_standalone_agent_api_key`; // Fleet debug routes diff --git a/x-pack/plugins/fleet/server/routes/otel/handlers.ts b/x-pack/plugins/fleet/server/routes/otel/handlers.ts new file mode 100644 index 0000000000000..a684763364c1b --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/otel/handlers.ts @@ -0,0 +1,63 @@ +/* + * 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 { TypeOf } from '@kbn/config-schema'; +import { v4 as uuidv4 } from 'uuid'; + +import type { FleetRequestHandler, OtelPolicySOAttributes } from '../../types'; +import type { CreateOtelPolicyRequestSchema } from '../../types/rest_spec/otel'; +import { defaultFleetErrorHandler } from '../../errors'; +import { OTEL_POLICY_SAVED_OBJECT_TYPE } from '../../../common/constants'; +import { appContextService } from '../../services'; + +export const createOtelPolicyHandler: FleetRequestHandler< + undefined, + undefined, + TypeOf +> = async (context, request, response) => { + const fleetContext = await context.fleet; + + const logger = appContextService.getLogger(); + const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; + const soClient = fleetContext.internalSoClient; + const { id, ...newPolicy } = request.body; + const policyId = id || uuidv4(); + + try { + const isoDate = new Date().toISOString(); + const newSo = await soClient.create( + OTEL_POLICY_SAVED_OBJECT_TYPE, + { + ...newPolicy, + revision: 1, + created_at: isoDate, + created_by: user?.username ?? 'system', + updated_at: isoDate, + updated_by: user?.username ?? 'system', + }, + { id: policyId } + ); + + // if (options?.bumpRevision ?? true) { + // for (const policyId of newPolicy.policy_ids) { + // await agentPolicyService.bumpRevision(soClient, esClient, policyId, { + // user: options?.user, + // }); + // } + // } + + const createdPolicy = { id: newSo.id, version: newSo.version, ...newSo.attributes }; + logger.debug(`Created new otel policy with id ${newSo.id} and version ${newSo.version}`); + + return response.ok({ + body: { + item: createdPolicy, + }, + }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/otel/index.ts b/x-pack/plugins/fleet/server/routes/otel/index.ts new file mode 100644 index 0000000000000..2ad4c337356fe --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/otel/index.ts @@ -0,0 +1,32 @@ +/* + * 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 { FleetAuthzRouter } from '../../services/security'; +import { API_VERSIONS, OTEL_POLICIES_ROUTES } from '../../../common/constants'; +import { CreateOtelPolicyRequestSchema } from '../../types/rest_spec/otel'; + +import { createOtelPolicyHandler } from './handlers'; + +export const registerRoutes = (router: FleetAuthzRouter) => { + // Create + router.versioned + .post({ + path: OTEL_POLICIES_ROUTES.CREATE_PATTERN, + description: 'Create new otel policy', + options: { + tags: ['oas-tag:Fleet Otel policies'], + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: CreateOtelPolicyRequestSchema, + }, + }, + createOtelPolicyHandler + ); +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 0eb6c86df01e2..b26821f39d0a2 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -12,6 +12,7 @@ import type { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-obje import { LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, + OTEL_POLICY_SAVED_OBJECT_TYPE, } from '../../common/constants'; import { @@ -792,6 +793,37 @@ export const getSavedObjectTypes = ( }, }, }, + [OTEL_POLICY_SAVED_OBJECT_TYPE]: { + name: OTEL_POLICY_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, + hidden: false, + namespaceType: 'multiple', + management: { + importableAndExportable: false, + }, + mappings: { + properties: { + name: { type: 'keyword' }, + description: { type: 'text' }, + namespace: { type: 'keyword' }, + policy_ids: { type: 'keyword' }, + output_id: { type: 'keyword' }, + integration: { + properties: { + name: { type: 'keyword' }, + version: { type: 'keyword' }, + }, + }, + vars: { type: 'flattened' }, + pipelines: { type: 'flattened' }, + revision: { type: 'integer' }, + updated_at: { type: 'date' }, + updated_by: { type: 'keyword' }, + created_at: { type: 'date' }, + created_by: { type: 'keyword' }, + }, + }, + }, [PACKAGES_SAVED_OBJECT_TYPE]: { name: PACKAGES_SAVED_OBJECT_TYPE, indexPattern: INGEST_SAVED_OBJECT_INDEX, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 6a586364f31d5..26cf5b06e970c 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -136,7 +136,7 @@ export async function getFullAgentPolicy( const packagePolicySecretReferences = (agentPolicy?.package_policies || []).flatMap( (policy) => policy.secret_references || [] ); - + // add data from otel policies const fullAgentPolicy: FullAgentPolicy = { id: agentPolicy.id, outputs: { diff --git a/x-pack/plugins/fleet/server/types/rest_spec/otel.ts b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts new file mode 100644 index 0000000000000..2de9a1003504e --- /dev/null +++ b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts @@ -0,0 +1,70 @@ +/* + * 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 { ConfigRecordSchema } from '../models'; + +const createOtelPolicyRequestBodySchema = schema.object({ + id: schema.maybe( + schema.string({ + meta: { + description: 'Package policy unique identifier', + }, + }) + ), + name: schema.string({ + meta: { + description: 'Otel policy name (should be unique)', + }, + }), + description: schema.maybe( + schema.string({ + meta: { + description: 'Otel policy description', + }, + }) + ), + namespace: schema.maybe( + schema.string({ + meta: { + description: 'Otel policy namespace', + }, + }) + ), + output_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + policy_ids: schema.arrayOf( + schema.string({ + meta: { + description: 'Agent policy IDs where that Otel policy will be added', + }, + }) + ), + integration: schema.maybe( + schema.object({ + name: schema.string({ + meta: { + description: 'Integration name', + }, + }), + version: schema.maybe( + schema.string({ + meta: { + description: 'Integration version', + }, + }) + ), + }) + ), + pipelines: schema.maybe(schema.arrayOf(schema.string())), + // reuse the vars schema defined for package policies + vars: schema.maybe(ConfigRecordSchema), +}); + +export const CreateOtelPolicyRequestSchema = { + body: createOtelPolicyRequestBodySchema, +}; diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 9be09fe4ee554..398aa16188513 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -139,6 +139,25 @@ export interface PackagePolicySOAttributes { overrides?: any | null; } +export interface OtelPolicySOAttributes { + name: string; + description?: string; + namespace?: string; + policy_ids: string[]; + output_id?: string | null; + integration?: { + name: string; + version?: string; + }; + vars?: PackagePolicyConfigRecord; + pipelines?: string[]; + revision: number; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; +} + interface OutputSoBaseAttributes { is_default: boolean; is_default_monitoring: boolean; From 4b06cad21f76a1a063ccbdfc1f6049edf4e98667 Mon Sep 17 00:00:00 2001 From: criamico Date: Tue, 24 Sep 2024 17:55:52 +0200 Subject: [PATCH 2/5] Make route work --- x-pack/plugins/fleet/server/routes/index.ts | 2 ++ .../plugins/fleet/server/routes/otel/index.ts | 3 +++ .../plugins/fleet/server/saved_objects/index.ts | 2 +- .../plugins/fleet/server/types/models/index.ts | 1 + .../plugins/fleet/server/types/models/otel.ts | 17 +++++++++++++++++ .../fleet/server/types/rest_spec/otel.ts | 5 ++--- .../plugins/fleet/server/types/so_attributes.ts | 2 +- 7 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 x-pack/plugins/fleet/server/types/models/otel.ts diff --git a/x-pack/plugins/fleet/server/routes/index.ts b/x-pack/plugins/fleet/server/routes/index.ts index 41ce57e85de2b..f9398eff5d79b 100644 --- a/x-pack/plugins/fleet/server/routes/index.ts +++ b/x-pack/plugins/fleet/server/routes/index.ts @@ -28,6 +28,7 @@ import { registerRoutes as registerMessageSigningServiceRoutes } from './message import { registerRoutes as registerUninstallTokenRoutes } from './uninstall_token'; import { registerRoutes as registerStandaloneAgentApiKeyRoutes } from './standalone_agent_api_key'; import { registerRoutes as registerDebugRoutes } from './debug'; +import { registerRoutes as registerOtelRoutes } from './otel'; export function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: FleetConfigType) { // Always register app routes for permissions checking @@ -51,6 +52,7 @@ export function registerRoutes(fleetAuthzRouter: FleetAuthzRouter, config: Fleet registerUninstallTokenRoutes(fleetAuthzRouter, config); registerStandaloneAgentApiKeyRoutes(fleetAuthzRouter); registerDebugRoutes(fleetAuthzRouter); + registerOtelRoutes(fleetAuthzRouter); // Conditional config routes if (config.agents.enabled) { diff --git a/x-pack/plugins/fleet/server/routes/otel/index.ts b/x-pack/plugins/fleet/server/routes/otel/index.ts index 2ad4c337356fe..97772e05244cc 100644 --- a/x-pack/plugins/fleet/server/routes/otel/index.ts +++ b/x-pack/plugins/fleet/server/routes/otel/index.ts @@ -15,6 +15,9 @@ export const registerRoutes = (router: FleetAuthzRouter) => { router.versioned .post({ path: OTEL_POLICIES_ROUTES.CREATE_PATTERN, + fleetAuthz: { + integrations: { writeIntegrationPolicies: true }, + }, description: 'Create new otel policy', options: { tags: ['oas-tag:Fleet Otel policies'], diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index b26821f39d0a2..57f1119302fc8 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -815,7 +815,7 @@ export const getSavedObjectTypes = ( }, }, vars: { type: 'flattened' }, - pipelines: { type: 'flattened' }, + pipelines: { type: 'keyword' }, revision: { type: 'integer' }, updated_at: { type: 'date' }, updated_by: { type: 'keyword' }, diff --git a/x-pack/plugins/fleet/server/types/models/index.ts b/x-pack/plugins/fleet/server/types/models/index.ts index 0a58538616f77..d874112efea5b 100644 --- a/x-pack/plugins/fleet/server/types/models/index.ts +++ b/x-pack/plugins/fleet/server/types/models/index.ts @@ -12,3 +12,4 @@ export * from './output'; export * from './enrollment_api_key'; export * from './preconfiguration'; export * from './download_sources'; +export * from './otel'; diff --git a/x-pack/plugins/fleet/server/types/models/otel.ts b/x-pack/plugins/fleet/server/types/models/otel.ts new file mode 100644 index 0000000000000..c39cb1d971985 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/models/otel.ts @@ -0,0 +1,17 @@ +/* + * 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'; + +export const VarsRecordSchema = schema.recordOf( + schema.string(), + schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), + { + meta: { + description: 'Otel policy variable', + }, + } +); diff --git a/x-pack/plugins/fleet/server/types/rest_spec/otel.ts b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts index 2de9a1003504e..c3b3f4b1d4b5c 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/otel.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; -import { ConfigRecordSchema } from '../models'; +import { VarsRecordSchema } from '../models'; const createOtelPolicyRequestBodySchema = schema.object({ id: schema.maybe( @@ -61,8 +61,7 @@ const createOtelPolicyRequestBodySchema = schema.object({ }) ), pipelines: schema.maybe(schema.arrayOf(schema.string())), - // reuse the vars schema defined for package policies - vars: schema.maybe(ConfigRecordSchema), + vars: schema.maybe(VarsRecordSchema), }); export const CreateOtelPolicyRequestSchema = { diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index 398aa16188513..fbf0f5043c93e 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -149,7 +149,7 @@ export interface OtelPolicySOAttributes { name: string; version?: string; }; - vars?: PackagePolicyConfigRecord; + vars?: Record; pipelines?: string[]; revision: number; created_at: string; From d2248e9f02c58d6e2e81d7df27f1fe97605835dd Mon Sep 17 00:00:00 2001 From: criamico Date: Wed, 25 Sep 2024 16:50:48 +0200 Subject: [PATCH 3/5] Create endpoint to upload an OTEL template in yml format --- .../fleet/common/constants/package_policy.ts | 1 + .../plugins/fleet/common/constants/routes.ts | 2 +- .../fleet/server/routes/otel/handlers.ts | 56 +++++++++++++++++-- .../plugins/fleet/server/routes/otel/index.ts | 44 +++++++++++++-- .../fleet/server/saved_objects/index.ts | 21 +++++++ .../fleet/server/types/rest_spec/otel.ts | 7 +++ .../fleet/server/types/so_attributes.ts | 9 +++ 7 files changed, 131 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/fleet/common/constants/package_policy.ts b/x-pack/plugins/fleet/common/constants/package_policy.ts index 82fca834027fd..8975756875782 100644 --- a/x-pack/plugins/fleet/common/constants/package_policy.ts +++ b/x-pack/plugins/fleet/common/constants/package_policy.ts @@ -19,3 +19,4 @@ export const inputsFormat = { export const LICENCE_FOR_MULTIPLE_AGENT_POLICIES = 'enterprise'; export const OTEL_POLICY_SAVED_OBJECT_TYPE = 'fleet-otel-policies'; +export const OTEL_INTEGRATION_SAVED_OBJECT_TYPE = 'fleet-otel-integrations'; diff --git a/x-pack/plugins/fleet/common/constants/routes.ts b/x-pack/plugins/fleet/common/constants/routes.ts index 892e063a7dc33..9fea00670beb7 100644 --- a/x-pack/plugins/fleet/common/constants/routes.ts +++ b/x-pack/plugins/fleet/common/constants/routes.ts @@ -218,7 +218,7 @@ export const DOWNLOAD_SOURCE_API_ROUTES = { // OTEL policies and templates routes export const OTEL_INTEGRATIONS_ROUTES = { - CREATE_PATTERN: `${OTEL_API_ROOT}/templates/{pkgName}`, + CREATE_PATTERN: `${OTEL_API_ROOT}/integrations/{name}`, }; export const OTEL_POLICIES_ROUTES = { CREATE_PATTERN: `${OTEL_API_ROOT}/policies`, diff --git a/x-pack/plugins/fleet/server/routes/otel/handlers.ts b/x-pack/plugins/fleet/server/routes/otel/handlers.ts index a684763364c1b..2109c969f6d63 100644 --- a/x-pack/plugins/fleet/server/routes/otel/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/otel/handlers.ts @@ -6,11 +6,22 @@ */ import type { TypeOf } from '@kbn/config-schema'; import { v4 as uuidv4 } from 'uuid'; +import { load } from 'js-yaml'; -import type { FleetRequestHandler, OtelPolicySOAttributes } from '../../types'; -import type { CreateOtelPolicyRequestSchema } from '../../types/rest_spec/otel'; +import type { + FleetRequestHandler, + OtelPolicySOAttributes, + OtelIntegrationSOAttributes, +} from '../../types'; +import type { + CreateOtelPolicyRequestSchema, + InstallOtelIntegrationRequestSchema, +} from '../../types/rest_spec/otel'; import { defaultFleetErrorHandler } from '../../errors'; -import { OTEL_POLICY_SAVED_OBJECT_TYPE } from '../../../common/constants'; +import { + OTEL_POLICY_SAVED_OBJECT_TYPE, + OTEL_INTEGRATION_SAVED_OBJECT_TYPE, +} from '../../../common/constants'; import { appContextService } from '../../services'; export const createOtelPolicyHandler: FleetRequestHandler< @@ -19,7 +30,6 @@ export const createOtelPolicyHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { const fleetContext = await context.fleet; - const logger = appContextService.getLogger(); const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; const soClient = fleetContext.internalSoClient; @@ -61,3 +71,41 @@ export const createOtelPolicyHandler: FleetRequestHandler< return defaultFleetErrorHandler({ error, response }); } }; + +export const createOtelIntegrationPolicyHandler: FleetRequestHandler< + TypeOf, + undefined, + TypeOf +> = async (context, request, response) => { + const fleetContext = await context.fleet; + const soClient = fleetContext.internalSoClient; + const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; + const logger = appContextService.getLogger(); + + const { name } = request.params; + const jsonConfig = request?.body ? load(request.body) : {}; + + try { + const isoDate = new Date().toISOString(); + const template = { + name, + config: jsonConfig, + }; + const newSo = await soClient.create( + OTEL_INTEGRATION_SAVED_OBJECT_TYPE, + { + ...template, + created_at: isoDate, + created_by: user?.username ?? 'system', + updated_at: isoDate, + updated_by: user?.username ?? 'system', + } + ); + logger.debug( + `Created new otel ${name} integration with id ${newSo.id} and version ${newSo.version}` + ); + return response.ok({ body: { item: template } }); + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/otel/index.ts b/x-pack/plugins/fleet/server/routes/otel/index.ts index 97772e05244cc..6ec8aaa4790af 100644 --- a/x-pack/plugins/fleet/server/routes/otel/index.ts +++ b/x-pack/plugins/fleet/server/routes/otel/index.ts @@ -5,13 +5,22 @@ * 2.0. */ import type { FleetAuthzRouter } from '../../services/security'; -import { API_VERSIONS, OTEL_POLICIES_ROUTES } from '../../../common/constants'; -import { CreateOtelPolicyRequestSchema } from '../../types/rest_spec/otel'; +import { + API_VERSIONS, + OTEL_POLICIES_ROUTES, + OTEL_INTEGRATIONS_ROUTES, +} from '../../../common/constants'; +import { + CreateOtelPolicyRequestSchema, + InstallOtelIntegrationRequestSchema, +} from '../../types/rest_spec/otel'; -import { createOtelPolicyHandler } from './handlers'; +const MAX_FILE_SIZE_BYTES = 104857600; // 100MB + +import { createOtelPolicyHandler, createOtelIntegrationPolicyHandler } from './handlers'; export const registerRoutes = (router: FleetAuthzRouter) => { - // Create + // Create policy router.versioned .post({ path: OTEL_POLICIES_ROUTES.CREATE_PATTERN, @@ -32,4 +41,31 @@ export const registerRoutes = (router: FleetAuthzRouter) => { }, createOtelPolicyHandler ); + + // create integration + router.versioned + .post({ + path: OTEL_INTEGRATIONS_ROUTES.CREATE_PATTERN, + fleetAuthz: { + integrations: { writeIntegrationPolicies: true }, + }, + description: 'Create new otel integration', + options: { + tags: ['oas-tag:Fleet Otel integrations'], + body: { + accepts: ['application/x-yaml', 'text/x-yaml'], + parse: false, + maxBytes: MAX_FILE_SIZE_BYTES, + }, + }, + }) + .addVersion( + { + version: API_VERSIONS.public.v1, + validate: { + request: InstallOtelIntegrationRequestSchema, + }, + }, + createOtelIntegrationPolicyHandler + ); }; diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 57f1119302fc8..1a4bfcff3f925 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -13,6 +13,7 @@ import { LEGACY_PACKAGE_POLICY_SAVED_OBJECT_TYPE, PACKAGE_POLICY_SAVED_OBJECT_TYPE, OTEL_POLICY_SAVED_OBJECT_TYPE, + OTEL_INTEGRATION_SAVED_OBJECT_TYPE, } from '../../common/constants'; import { @@ -824,6 +825,26 @@ export const getSavedObjectTypes = ( }, }, }, + [OTEL_INTEGRATION_SAVED_OBJECT_TYPE]: { + name: OTEL_INTEGRATION_SAVED_OBJECT_TYPE, + indexPattern: INGEST_SAVED_OBJECT_INDEX, + hidden: false, + namespaceType: 'multiple', + management: { + importableAndExportable: false, + }, + mappings: { + properties: { + name: { type: 'keyword' }, + version: { type: 'keyword' }, + config: { type: 'flattened' }, + updated_at: { type: 'date' }, + updated_by: { type: 'keyword' }, + created_at: { type: 'date' }, + created_by: { type: 'keyword' }, + }, + }, + }, [PACKAGES_SAVED_OBJECT_TYPE]: { name: PACKAGES_SAVED_OBJECT_TYPE, indexPattern: INGEST_SAVED_OBJECT_INDEX, diff --git a/x-pack/plugins/fleet/server/types/rest_spec/otel.ts b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts index c3b3f4b1d4b5c..e6cc1c033545f 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/otel.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/otel.ts @@ -67,3 +67,10 @@ const createOtelPolicyRequestBodySchema = schema.object({ export const CreateOtelPolicyRequestSchema = { body: createOtelPolicyRequestBodySchema, }; + +export const InstallOtelIntegrationRequestSchema = { + params: schema.object({ + name: schema.string(), + }), + body: schema.any(), +}; diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index fbf0f5043c93e..b6630bd4bd5d6 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -157,6 +157,15 @@ export interface OtelPolicySOAttributes { updated_at: string; updated_by: string; } +export interface OtelIntegrationSOAttributes { + name: string; + version?: string; + config?: any; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; +} interface OutputSoBaseAttributes { is_default: boolean; From 173b31bd59f1c675db63d4eda1c762ded8de34c5 Mon Sep 17 00:00:00 2001 From: criamico Date: Thu, 26 Sep 2024 15:09:21 +0200 Subject: [PATCH 4/5] Merge otel policies to full agent policy --- .../fleet/common/types/models/agent_policy.ts | 2 + .../plugins/fleet/common/types/models/otel.ts | 35 ++++++++++++++++ .../fleet/server/routes/otel/handlers.ts | 18 +++++---- .../agent_policies/full_agent_policy.ts | 8 +++- .../otel_policies_to_agent_inputs.ts | 40 +++++++++++++++++++ .../fleet/server/services/agent_policy.ts | 8 ++++ .../fleet/server/services/otel_policy.ts | 39 ++++++++++++++++++ .../server/services/spaces/agent_policy.ts | 1 + 8 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/fleet/common/types/models/otel.ts create mode 100644 x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts create mode 100644 x-pack/plugins/fleet/server/services/otel_policy.ts diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index e10703f6fa0c5..eb1e64d418f1a 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -12,6 +12,7 @@ import type { MonitoringType, PolicySecretReference, ValueOf } from '..'; import type { PackagePolicy, PackagePolicyPackage } from './package_policy'; import type { Output } from './output'; +import type { OtelPolicy } from './otel'; export type AgentPolicyStatus = typeof agentPolicyStatuses; @@ -74,6 +75,7 @@ export interface AgentPolicy extends Omit { space_ids?: string[] | undefined; status: ValueOf; package_policies?: PackagePolicy[]; + otel_policies?: OtelPolicy[]; is_managed: boolean; // required for created policy updated_at: string; updated_by: string; diff --git a/x-pack/plugins/fleet/common/types/models/otel.ts b/x-pack/plugins/fleet/common/types/models/otel.ts new file mode 100644 index 0000000000000..2f015989e4533 --- /dev/null +++ b/x-pack/plugins/fleet/common/types/models/otel.ts @@ -0,0 +1,35 @@ +/* + * 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 interface OtelPolicy { + name: string; + description?: string; + namespace?: string; + policy_ids: string[]; + output_id?: string | null; + integration?: { + name: string; + version?: string; + }; + vars?: Record; + pipelines?: string[]; + revision: number; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; +} + +export interface OtelInputReceivers { + receivers?: { + [extendedName: string]: { + name?: string; + pipelines?: string[]; + parameters?: Record; + }; + }; +} diff --git a/x-pack/plugins/fleet/server/routes/otel/handlers.ts b/x-pack/plugins/fleet/server/routes/otel/handlers.ts index 2109c969f6d63..555488f0eb712 100644 --- a/x-pack/plugins/fleet/server/routes/otel/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/otel/handlers.ts @@ -22,7 +22,7 @@ import { OTEL_POLICY_SAVED_OBJECT_TYPE, OTEL_INTEGRATION_SAVED_OBJECT_TYPE, } from '../../../common/constants'; -import { appContextService } from '../../services'; +import { agentPolicyService, appContextService } from '../../services'; export const createOtelPolicyHandler: FleetRequestHandler< undefined, @@ -30,9 +30,12 @@ export const createOtelPolicyHandler: FleetRequestHandler< TypeOf > = async (context, request, response) => { const fleetContext = await context.fleet; + const coreContext = await context.core; const logger = appContextService.getLogger(); const user = appContextService.getSecurityCore().authc.getCurrentUser(request) || undefined; const soClient = fleetContext.internalSoClient; + const esClient = coreContext.elasticsearch.client.asInternalUser; + const { id, ...newPolicy } = request.body; const policyId = id || uuidv4(); @@ -51,13 +54,12 @@ export const createOtelPolicyHandler: FleetRequestHandler< { id: policyId } ); - // if (options?.bumpRevision ?? true) { - // for (const policyId of newPolicy.policy_ids) { - // await agentPolicyService.bumpRevision(soClient, esClient, policyId, { - // user: options?.user, - // }); - // } - // } + // bump agent policy revision + for (const newPolicyId of newPolicy.policy_ids) { + await agentPolicyService.bumpRevision(soClient, esClient, newPolicyId, { + user, + }); + } const createdPolicy = { id: newSo.id, version: newSo.version, ...newSo.attributes }; logger.debug(`Created new otel policy with id ${newSo.id} and version ${newSo.version}`); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 26cf5b06e970c..1624fb7f33889 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -53,6 +53,7 @@ import { DEFAULT_CLUSTER_PERMISSIONS, } from './package_policies_to_agent_permissions'; import { fetchRelatedSavedObjects } from './related_saved_objects'; +import { otelPoliciesToAgentInputs } from './otel_policies_to_agent_inputs'; async function fetchAgentPolicy(soClient: SavedObjectsClientContract, id: string) { try { @@ -136,7 +137,7 @@ export async function getFullAgentPolicy( const packagePolicySecretReferences = (agentPolicy?.package_policies || []).flatMap( (policy) => policy.secret_references || [] ); - // add data from otel policies + const fullAgentPolicy: FullAgentPolicy = { id: agentPolicy.id, outputs: { @@ -309,6 +310,11 @@ export async function getFullAgentPolicy( return deepMerge(fullAgentPolicy, agentPolicy.overrides); } + const otelInputs = otelPoliciesToAgentInputs(agentPolicy); + if (otelInputs) { + return deepMerge(fullAgentPolicy, otelInputs); + } + return fullAgentPolicy; } diff --git a/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts new file mode 100644 index 0000000000000..19155cb3582a7 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts @@ -0,0 +1,40 @@ +/* + * 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 { v4 as uuidV4 } from 'uuid'; + +import type { OtelPolicy, OtelInputReceivers } from '../../../common/types/models/otel'; +import type { AgentPolicy } from '../../types'; +const DEFAULT_PATH = '.'; + +export const otelPoliciesToAgentInputs = (agentPolicy: AgentPolicy) => { + if (!agentPolicy?.otel_policies) return; + + let parsedInputs = {}; + for (const otelPolicy of agentPolicy.otel_policies) { + parsedInputs = { ...parsedInputs, ...otelPolicyToAgentInput(otelPolicy) }; + } + return parsedInputs; +}; + +const otelPolicyToAgentReceivers = (otelPolicy: OtelPolicy) => { + const uniqueIntegrationName = `integrations/${otelPolicy?.integration?.name}-${uuidV4()}`; + const data = { + ...(otelPolicy?.integration?.name ? { name: otelPolicy.integration.name } : null), + ...(otelPolicy?.integration?.version ? { version: otelPolicy.integration.version } : null), + ...(otelPolicy?.pipelines ? { pipelines: otelPolicy.pipelines } : null), + ...(otelPolicy?.vars ? { parameters: otelPolicy.vars } : null), + }; + const result: OtelInputReceivers = { receivers: { [uniqueIntegrationName]: data } }; + return result; +}; + +const otelPolicyToAgentInput = (otelPolicy: OtelPolicy) => { + const extensions = { extensions: { file_integrations: `${DEFAULT_PATH}` } }; + const receivers = otelPolicyToAgentReceivers(otelPolicy); + + return { ...extensions, ...receivers }; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 9f9a38bebfef6..60b61d695c312 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -114,6 +114,7 @@ import { isAgentlessEnabled } from './utils/agentless'; import { validatePolicyNamespaceForSpace } from './spaces/policy_namespaces'; import { isSpaceAwarenessEnabled } from './spaces/helpers'; import { scheduleDeployAgentPoliciesTask } from './agent_policies/deploy_agent_policies_task'; +import { findAllOtelPoliciesForAgentPolicy } from './otel_policy'; const KEY_EDITABLE_FOR_MANAGED_POLICIES = ['namespace']; @@ -220,6 +221,8 @@ class AgentPolicyService { const newAgentPolicy = await this.get(soClient, id, false); newAgentPolicy!.package_policies = existingAgentPolicy.package_policies; + // Update otel_policies + newAgentPolicy!.otel_policies = existingAgentPolicy.otel_policies; if (options.bumpRevision || options.removeProtection) { await this.triggerAgentPolicyUpdatedEvent(esClient, 'updated', id, { @@ -455,6 +458,8 @@ class AgentPolicyService { if (withPackagePolicies) { agentPolicy.package_policies = (await packagePolicyService.findAllForAgentPolicy(soClient, id)) || []; + + agentPolicy.otel_policies = (await findAllOtelPoliciesForAgentPolicy(soClient, id)) || []; } auditLoggingService.writeCustomSoAuditLog({ @@ -607,6 +612,8 @@ class AgentPolicyService { if (withPackagePolicies) { agentPolicy.package_policies = (await packagePolicyService.findAllForAgentPolicy(soClient, agentPolicy.id)) || []; + agentPolicy.otel_policies = + (await findAllOtelPoliciesForAgentPolicy(soClient, agentPolicy.id)) || []; } if (options.withAgentCount) { await getAgentsByKuery(appContextService.getInternalUserESClient(), soClient, { @@ -1151,6 +1158,7 @@ class AgentPolicyService { } const packagePolicies = await packagePolicyService.findAllForAgentPolicy(soClient, id); + // TODO: should handle otel_policies as well if (packagePolicies.length) { const hasManagedPackagePolicies = packagePolicies.some( diff --git a/x-pack/plugins/fleet/server/services/otel_policy.ts b/x-pack/plugins/fleet/server/services/otel_policy.ts new file mode 100644 index 0000000000000..c9eb07566239d --- /dev/null +++ b/x-pack/plugins/fleet/server/services/otel_policy.ts @@ -0,0 +1,39 @@ +/* + * 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 { SavedObjectsClientContract } from '@kbn/core/server'; + +import type { OtelPolicy } from '../../common/types/models/otel'; +import { SO_SEARCH_LIMIT } from '../constants'; +import { OTEL_POLICY_SAVED_OBJECT_TYPE } from '../../common/constants'; +import type { OtelPolicySOAttributes } from '../types'; + +import { escapeSearchQueryPhrase } from './saved_object'; + +export async function findAllOtelPoliciesForAgentPolicy( + soClient: SavedObjectsClientContract, + agentPolicyId: string +): Promise { + const otelPolicySO = await soClient.find({ + type: OTEL_POLICY_SAVED_OBJECT_TYPE, + filter: `${OTEL_POLICY_SAVED_OBJECT_TYPE}.attributes.policy_ids:${escapeSearchQueryPhrase( + agentPolicyId + )}`, + perPage: SO_SEARCH_LIMIT, + }); + if (!otelPolicySO) { + return []; + } + + const otelPolicies = otelPolicySO.saved_objects.map((so) => ({ + id: so.id, + version: so.version, + ...so.attributes, + })); + + return otelPolicies; +} diff --git a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts index f488a89297265..442c628e00251 100644 --- a/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/spaces/agent_policy.ts @@ -49,6 +49,7 @@ export async function updateAgentPolicySpaces({ currentSpaceSoClient, agentPolicyId ); + // TODO: should handle otel_policies as well if (deepEqual(existingPolicy?.space_ids?.sort() ?? [DEFAULT_SPACE_ID], newSpaceIds.sort())) { return; From 3d4a6c04d5d87ebbcac8e805d6002f9f18e890bd Mon Sep 17 00:00:00 2001 From: criamico Date: Thu, 26 Sep 2024 17:18:42 +0200 Subject: [PATCH 5/5] Nest otel configuration under inputs and retrieve template --- .../plugins/fleet/common/types/models/otel.ts | 8 ++++ .../agent_policies/full_agent_policy.ts | 10 ++--- .../otel_policies_to_agent_inputs.ts | 40 ++++++++++++++----- .../fleet/server/services/otel_policy.ts | 24 ++++++++++- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/otel.ts b/x-pack/plugins/fleet/common/types/models/otel.ts index 2f015989e4533..6e1646a074741 100644 --- a/x-pack/plugins/fleet/common/types/models/otel.ts +++ b/x-pack/plugins/fleet/common/types/models/otel.ts @@ -6,6 +6,7 @@ */ export interface OtelPolicy { + id: string; name: string; description?: string; namespace?: string; @@ -33,3 +34,10 @@ export interface OtelInputReceivers { }; }; } + +export interface OtelInputExtensions { + extensions?: { + config_integrations?: { integrations?: string }; + file_integrations?: { path?: string }; + }; +} diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 1624fb7f33889..33270d7290164 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -121,13 +121,16 @@ export async function getFullAgentPolicy( }) ); - const inputs = await storedPackagePoliciesToAgentInputs( + const packagePolicyInputs = await storedPackagePoliciesToAgentInputs( agentPolicy.package_policies as PackagePolicy[], packageInfoCache, getOutputIdForAgentPolicy(dataOutput), agentPolicy.namespace, agentPolicy.global_data_tags ); + const otelInputs = await otelPoliciesToAgentInputs(soClient, agentPolicy); + const inputs = [...packagePolicyInputs, ...(otelInputs ? otelInputs : [])]; + const features = (agentPolicy.agent_features || []).reduce((acc, { name, ...featureConfig }) => { acc[name] = featureConfig; return acc; @@ -310,11 +313,6 @@ export async function getFullAgentPolicy( return deepMerge(fullAgentPolicy, agentPolicy.overrides); } - const otelInputs = otelPoliciesToAgentInputs(agentPolicy); - if (otelInputs) { - return deepMerge(fullAgentPolicy, otelInputs); - } - return fullAgentPolicy; } diff --git a/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts b/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts index 19155cb3582a7..f7b2a08250fe1 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/otel_policies_to_agent_inputs.ts @@ -4,37 +4,55 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { v4 as uuidV4 } from 'uuid'; +import type { SavedObjectsClientContract } from '@kbn/core/server'; -import type { OtelPolicy, OtelInputReceivers } from '../../../common/types/models/otel'; +import type { + OtelPolicy, + OtelInputReceivers, + OtelInputExtensions, +} from '../../../common/types/models/otel'; import type { AgentPolicy } from '../../types'; -const DEFAULT_PATH = '.'; +import { findAndEmbedIntegrations } from '../otel_policy'; -export const otelPoliciesToAgentInputs = (agentPolicy: AgentPolicy) => { +export const otelPoliciesToAgentInputs = async ( + soClient: SavedObjectsClientContract, + agentPolicy: AgentPolicy +) => { if (!agentPolicy?.otel_policies) return; - let parsedInputs = {}; + const parsedInputs = []; for (const otelPolicy of agentPolicy.otel_policies) { - parsedInputs = { ...parsedInputs, ...otelPolicyToAgentInput(otelPolicy) }; + parsedInputs.push(...(await otelPolicyToAgentInput(soClient, otelPolicy))); } return parsedInputs; }; const otelPolicyToAgentReceivers = (otelPolicy: OtelPolicy) => { - const uniqueIntegrationName = `integrations/${otelPolicy?.integration?.name}-${uuidV4()}`; + const inputId = `integrations/${otelPolicy?.integration?.name}-${otelPolicy.id}`; const data = { ...(otelPolicy?.integration?.name ? { name: otelPolicy.integration.name } : null), ...(otelPolicy?.integration?.version ? { version: otelPolicy.integration.version } : null), ...(otelPolicy?.pipelines ? { pipelines: otelPolicy.pipelines } : null), ...(otelPolicy?.vars ? { parameters: otelPolicy.vars } : null), }; - const result: OtelInputReceivers = { receivers: { [uniqueIntegrationName]: data } }; + const result: OtelInputReceivers = { receivers: { [inputId]: data } }; return result; }; -const otelPolicyToAgentInput = (otelPolicy: OtelPolicy) => { - const extensions = { extensions: { file_integrations: `${DEFAULT_PATH}` } }; +const otelPolicyToAgentInput = async ( + soClient: SavedObjectsClientContract, + otelPolicy: OtelPolicy +) => { + let extensions: OtelInputExtensions = {}; + // TODO: move this query to otel policy service + if (otelPolicy?.integration?.name) { + const template = await findAndEmbedIntegrations(soClient, otelPolicy?.integration?.name); + extensions = { + extensions: { config_integrations: { integrations: template } }, + }; + } + const receivers = otelPolicyToAgentReceivers(otelPolicy); - return { ...extensions, ...receivers }; + return [{ ...extensions, ...receivers }]; }; diff --git a/x-pack/plugins/fleet/server/services/otel_policy.ts b/x-pack/plugins/fleet/server/services/otel_policy.ts index c9eb07566239d..7f5e1b911b56d 100644 --- a/x-pack/plugins/fleet/server/services/otel_policy.ts +++ b/x-pack/plugins/fleet/server/services/otel_policy.ts @@ -9,8 +9,11 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import type { OtelPolicy } from '../../common/types/models/otel'; import { SO_SEARCH_LIMIT } from '../constants'; -import { OTEL_POLICY_SAVED_OBJECT_TYPE } from '../../common/constants'; -import type { OtelPolicySOAttributes } from '../types'; +import { + OTEL_POLICY_SAVED_OBJECT_TYPE, + OTEL_INTEGRATION_SAVED_OBJECT_TYPE, +} from '../../common/constants'; +import type { OtelPolicySOAttributes, OtelIntegrationSOAttributes } from '../types'; import { escapeSearchQueryPhrase } from './saved_object'; @@ -25,6 +28,7 @@ export async function findAllOtelPoliciesForAgentPolicy( )}`, perPage: SO_SEARCH_LIMIT, }); + if (!otelPolicySO) { return []; } @@ -37,3 +41,19 @@ export async function findAllOtelPoliciesForAgentPolicy( return otelPolicies; } + +// This function finds by name the latest template uploaded through the otel/integrations endpoint and extracts the config +export async function findAndEmbedIntegrations(soClient: SavedObjectsClientContract, name: string) { + const otelIntegrationSO = await soClient.find({ + type: OTEL_INTEGRATION_SAVED_OBJECT_TYPE, + filter: `${OTEL_INTEGRATION_SAVED_OBJECT_TYPE}.attributes.name:${name}`, + perPage: SO_SEARCH_LIMIT, + sortField: 'updated_at', + sortOrder: 'desc', + }); + + if (!otelIntegrationSO || otelIntegrationSO?.saved_objects.length === 0) { + return {}; + } + return otelIntegrationSO?.saved_objects[0].attributes.config; +}