From a00f884c98a15476d732bcd71d7b6b116a91c51e Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 23 Mar 2026 11:28:32 +0100 Subject: [PATCH 01/32] [Fleet] Extend OTel exporter configuration --- .../agent_policies/full_agent_policy.test.ts | 133 +++++++++++++++ .../agent_policies/full_agent_policy.ts | 10 +- .../agent_policies/otel_collector.test.ts | 157 ++++++++++++++++++ .../services/agent_policies/otel_collector.ts | 71 ++++++-- 4 files changed, 359 insertions(+), 12 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts index 2a61c717db02b..8b5c351babb6c 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -1962,6 +1962,139 @@ describe('getFullAgentPolicy', () => { expect(mockedGenerateOtelcolConfig).not.toHaveBeenCalled(); }); + + it('should pass the resolved proxy to generateOtelcolConfig when dataOutput has a proxy_id', async () => { + const proxy = { + id: 'proxy-1', + name: 'my-proxy', + url: 'http://proxy.example.com:3128', + proxy_headers: { 'X-Custom': 'value' }, + is_preconfigured: false, + }; + + mockedFetchRelatedSavedObjects.mockResolvedValue({ + outputs: [ + { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + proxy_id: 'proxy-1', + }, + ], + proxies: [proxy], + dataOutput: { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + proxy_id: 'proxy-1', + }, + monitoringOutput: { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }, + downloadSource: { + id: 'default-download-source-id', + is_default: true, + name: 'Default host', + host: 'http://default-registry.co', + }, + downloadSourceProxy: undefined, + fleetServerHost: { + name: 'default Fleet Server', + id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', + is_default: true, + host_urls: ['http://fleetserver:8220'], + is_preconfigured: false, + }, + }); + + mockAgentPolicy({ package_policies: [] }); + + await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); + + expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; + // Fourth argument should be the resolved proxy + expect(callArgs[3]).toEqual(proxy); + }); + + it('should pass undefined proxy to generateOtelcolConfig when dataOutput has no proxy_id', async () => { + mockAgentPolicy({ package_policies: [] }); + + await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); + + expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; + // Fourth argument should be undefined when no proxy_id + expect(callArgs[3]).toBeUndefined(); + }); + + it('should pass undefined proxy to generateOtelcolConfig when proxy_id does not match any proxy', async () => { + mockedFetchRelatedSavedObjects.mockResolvedValue({ + outputs: [ + { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + proxy_id: 'nonexistent-proxy', + }, + ], + proxies: [], + dataOutput: { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + proxy_id: 'nonexistent-proxy', + }, + monitoringOutput: { + id: 'test-id', + is_default: true, + is_default_monitoring: true, + name: 'default', + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }, + downloadSource: { + id: 'default-download-source-id', + is_default: true, + name: 'Default host', + host: 'http://default-registry.co', + }, + downloadSourceProxy: undefined, + fleetServerHost: { + name: 'default Fleet Server', + id: '93f74c0-e876-11ea-b7d3-8b2acec6f75c', + is_default: true, + host_urls: ['http://fleetserver:8220'], + is_preconfigured: false, + }, + }); + + mockAgentPolicy({ package_policies: [] }); + + await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); + + expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; + // Fourth argument should be undefined when proxy_id doesn't match + expect(callArgs[3]).toBeUndefined(); + }); }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts index 45fc09c82c997..8d97ed64fb392 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts @@ -163,7 +163,15 @@ export async function getFullAgentPolicy( let otelcolConfig; if (experimentalFeature.enableOtelIntegrations) { - otelcolConfig = generateOtelcolConfig(agentInputs, dataOutput, packageInfoCache); + const dataOutputProxy = dataOutput?.proxy_id + ? proxies.find((p) => p.id === dataOutput.proxy_id) + : undefined; + otelcolConfig = generateOtelcolConfig( + agentInputs, + dataOutput, + packageInfoCache, + dataOutputProxy + ); } const inputs = agentInputs diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index e6203463f99d6..488056ef9407f 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -255,6 +255,9 @@ describe('generateOtelcolConfig', () => { it('should return the otel config when there is one', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + extensions: { + 'beatsauth/default': {}, + }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -287,9 +290,11 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, }, }, service: { + extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -308,6 +313,9 @@ describe('generateOtelcolConfig', () => { it('should use the output id when it is not the default', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; expect(generateOtelcolConfig(inputs, { ...defaultOutput, is_default: false })).toEqual({ + extensions: { + 'beatsauth/fleet-default-output': {}, + }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -340,9 +348,11 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/fleet-default-output': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/fleet-default-output' }, }, }, service: { + extensions: ['beatsauth/fleet-default-output'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -361,6 +371,9 @@ describe('generateOtelcolConfig', () => { it('should return the otel config if there is any', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + extensions: { + 'beatsauth/default': {}, + }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -393,9 +406,11 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, }, }, service: { + extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -454,6 +469,9 @@ describe('generateOtelcolConfig', () => { it('should merge otel configs', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1, otelInput2]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + extensions: { + 'beatsauth/default': {}, + }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -508,9 +526,11 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, }, }, service: { + extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -534,6 +554,9 @@ describe('generateOtelcolConfig', () => { it('should keep components with the same type', () => { const inputs: FullAgentPolicyInput[] = [otelInputMultipleComponentsSameType]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + extensions: { + 'beatsauth/default': {}, + }, receivers: { 'httpcheck/1/test-3-stream-id-1': { targets: [ @@ -576,9 +599,11 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, }, }, service: { + extensions: ['beatsauth/default'], pipelines: { 'metrics/test-3-stream-id-1': { receivers: ['httpcheck/1/test-3-stream-id-1', 'httpcheck/2/test-3-stream-id-1'], @@ -640,12 +665,17 @@ describe('generateOtelcolConfig', () => { 'elasticapm/apmtest': {}, forward: {}, }, + extensions: { + 'beatsauth/default': {}, + }, exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, }, }, service: { + extensions: ['beatsauth/default'], pipelines: { 'traces/test-traces-stream-id-1': { receivers: ['zipkin/test-traces-stream-id-1'], @@ -1230,4 +1260,131 @@ describe('generateOtelcolConfig', () => { }); }); }); + + describe('beatsauth extension generation', () => { + const inputs: FullAgentPolicyInput[] = [otelInput1]; + + it('should include beatsauth extension with ssl fields when output has ssl config', () => { + const outputWithSSL: Output = { + ...defaultOutput, + ca_trusted_fingerprint: 'abc123fingerprint', + ssl: { + certificate_authorities: ['-----BEGIN CERTIFICATE-----\nMIIC...'], + certificate: '-----BEGIN CERTIFICATE-----\nMIID...', + key: '-----BEGIN PRIVATE KEY-----\nMIIE...', + verification_mode: 'full', + }, + }; + + const result = generateOtelcolConfig(inputs, outputWithSSL); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + ssl: { + ca_trusted_fingerprint: 'abc123fingerprint', + certificate_authorities: ['-----BEGIN CERTIFICATE-----\nMIIC...'], + certificate: '-----BEGIN CERTIFICATE-----\nMIID...', + key: '-----BEGIN PRIVATE KEY-----\nMIIE...', + verification_mode: 'full', + }, + }); + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: ['http://localhost:9200'], + auth: { authenticator: 'beatsauth/default' }, + }); + expect(result.service?.extensions).toContain('beatsauth/default'); + }); + + it('should include ca_trusted_fingerprint only when that is the only ssl field set', () => { + const outputWithFingerprint: Output = { + ...defaultOutput, + ca_trusted_fingerprint: 'myfingerprint', + }; + + const result = generateOtelcolConfig(inputs, outputWithFingerprint); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + ssl: { ca_trusted_fingerprint: 'myfingerprint' }, + }); + }); + + it('should include ca_sha256 in beatsauth ssl config', () => { + const outputWithCaSha: Output = { + ...defaultOutput, + ca_sha256: 'sha256value', + }; + + const result = generateOtelcolConfig(inputs, outputWithCaSha); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + ssl: { ca_sha256: 'sha256value' }, + }); + }); + + it('should include proxy fields when proxy is passed', () => { + const proxy = { + id: 'proxy-1', + name: 'my-proxy', + url: 'http://proxy.example.com:3128', + proxy_headers: { 'X-Custom-Header': 'value' }, + is_preconfigured: false, + }; + + const result = generateOtelcolConfig(inputs, defaultOutput, undefined, proxy); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + proxy_url: 'http://proxy.example.com:3128', + proxy_headers: { 'X-Custom-Header': 'value' }, + }); + expect(result.service?.extensions).toContain('beatsauth/default'); + }); + + it('should include proxy url but not proxy_headers when headers are not set', () => { + const proxy = { + id: 'proxy-1', + name: 'my-proxy', + url: 'http://proxy.example.com:3128', + is_preconfigured: false, + }; + + const result = generateOtelcolConfig(inputs, defaultOutput, undefined, proxy); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + proxy_url: 'http://proxy.example.com:3128', + }); + }); + + it('should combine ssl and proxy fields in beatsauth when both are configured', () => { + const outputWithSSL: Output = { + ...defaultOutput, + ca_trusted_fingerprint: 'combinedfingerprint', + ssl: { + certificate_authorities: ['-----BEGIN CERTIFICATE-----\nCA...'], + }, + }; + const proxy = { + id: 'proxy-1', + name: 'my-proxy', + url: 'http://proxy.example.com:3128', + proxy_headers: { 'Proxy-Auth': 'token' }, + is_preconfigured: false, + }; + + const result = generateOtelcolConfig(inputs, outputWithSSL, undefined, proxy); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + ssl: { + ca_trusted_fingerprint: 'combinedfingerprint', + certificate_authorities: ['-----BEGIN CERTIFICATE-----\nCA...'], + }, + proxy_url: 'http://proxy.example.com:3128', + proxy_headers: { 'Proxy-Auth': 'token' }, + }); + }); + + it('should produce empty beatsauth config when output has no ssl or proxy fields', () => { + const result = generateOtelcolConfig(inputs, defaultOutput); + + expect(result.extensions?.['beatsauth/default']).toEqual({}); + }); + }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index 6faa182c64edc..71c8d8f1932df 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { Output, TemplateAgentPolicyInput } from '../../types'; +import type { FleetProxy, Output, TemplateAgentPolicyInput } from '../../types'; import type { FullAgentPolicyInput, OTelCollectorComponentID, @@ -29,7 +29,8 @@ import { hasDynamicSignalTypes } from '../epm/packages/input_type_packages'; export function generateOtelcolConfig( inputs: FullAgentPolicyInput[] | TemplateAgentPolicyInput[], dataOutput?: Output, - packageInfoCache?: Map + packageInfoCache?: Map, + proxy?: FleetProxy ): OTelCollectorConfig { const otelConfigs: OTelCollectorConfig[] = inputs .filter((input) => input.type === OTEL_COLLECTOR_INPUT_TYPE) @@ -130,7 +131,7 @@ export function generateOtelcolConfig( } const config = mergeOtelcolConfigs(otelConfigs); - return attachOtelcolExporter(config, dataOutput); + return attachOtelcolExporter(config, dataOutput, proxy); } function generateOtelTypeTransforms( @@ -374,24 +375,58 @@ function mergeOtelcolConfigs(otelConfigs: OTelCollectorConfig[]): OTelCollectorC }); } +function buildBeatsauthConfig(output: Output, proxy?: FleetProxy): Record { + const config: Record = {}; + + const ssl: Record = {}; + if (output.ca_trusted_fingerprint) ssl.ca_trusted_fingerprint = output.ca_trusted_fingerprint; + if (output.ca_sha256) ssl.ca_sha256 = output.ca_sha256; + if (output.ssl?.certificate_authorities?.length) + ssl.certificate_authorities = output.ssl.certificate_authorities; + if (output.ssl?.certificate) ssl.certificate = output.ssl.certificate; + if (output.ssl?.key) ssl.key = output.ssl.key; + if (output.ssl?.verification_mode) ssl.verification_mode = output.ssl.verification_mode; + if (Object.keys(ssl).length > 0) config.ssl = ssl; + + if (proxy) { + config.proxy_url = proxy.url; + if (proxy.proxy_headers) config.proxy_headers = proxy.proxy_headers; + } + + return config; +} + function attachOtelcolExporter( config: OTelCollectorConfig, - dataOutput?: Output + dataOutput?: Output, + proxy?: FleetProxy ): OTelCollectorConfig { if (!dataOutput) { return config; } - const exporter = generateOtelcolExporter(dataOutput); + const { extensions, exporters } = generateOtelcolExporter(dataOutput, proxy); config.connectors = { ...config.connectors, forward: {}, }; + config.extensions = { + ...config.extensions, + ...extensions, + }; config.exporters = { ...config.exporters, - ...exporter, + ...exporters, }; + const extensionIDs = Object.keys(extensions); + if (extensionIDs.length > 0) { + config.service = { + ...config.service, + extensions: [...(config.service?.extensions ?? []), ...extensionIDs], + }; + } + if (config.service?.pipelines) { const signalTypes = new Set(); Object.entries(config.service.pipelines).forEach(([id, pipeline]) => { @@ -405,7 +440,7 @@ function attachOtelcolExporter( signalTypes.forEach((id) => { config.service!.pipelines![id] = { receivers: ['forward'], - exporters: Object.keys(exporter), + exporters: Object.keys(exporters), }; }); } @@ -413,15 +448,29 @@ function attachOtelcolExporter( return config; } -function generateOtelcolExporter(dataOutput: Output): Record { +function generateOtelcolExporter( + dataOutput: Output, + proxy?: FleetProxy +): { + extensions: Record; + exporters: Record; +} { switch (dataOutput.type) { - case outputType.Elasticsearch: + case outputType.Elasticsearch: { const outputID = getOutputIdForAgentPolicy(dataOutput); + const beatsauthID = `beatsauth/${outputID}`; return { - [`elasticsearch/${outputID}`]: { - endpoints: dataOutput.hosts, + extensions: { + [beatsauthID]: buildBeatsauthConfig(dataOutput, proxy), + }, + exporters: { + [`elasticsearch/${outputID}`]: { + endpoints: dataOutput.hosts, + auth: { authenticator: beatsauthID }, + }, }, }; + } default: throw new FleetError( `output type ${dataOutput.type} not supported when policy contains OTel inputs` From 9e8243d3500df8679e3bfd5eed359c1209be41a8 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 24 Mar 2026 12:16:46 +0100 Subject: [PATCH 02/32] Add advanced section --- .../fleet/common/types/models/output.ts | 1 + .../edit_output_flyout/index.test.tsx | 128 ++++++++++++++++++ .../components/edit_output_flyout/index.tsx | 40 +++++- .../edit_output_flyout/use_output_form.tsx | 12 ++ .../fleet/server/saved_objects/index.ts | 1 + .../agent_policies/otel_collector.test.ts | 56 ++++++++ .../services/agent_policies/otel_collector.ts | 7 + .../fleet/server/types/models/output.ts | 1 + .../fleet/server/types/so_attributes.ts | 1 + 9 files changed, 246 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts index ae4cb15e060b5..e37ae965e3dea 100644 --- a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts @@ -54,6 +54,7 @@ interface NewBaseOutput { export interface NewElasticsearchOutput extends NewBaseOutput { type: OutputType['Elasticsearch']; + otel_exporter_config_yaml?: string | null; } export interface NewRemoteElasticsearchOutput extends NewBaseOutput { diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index eb20419b35629..36b87738a141b 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -481,6 +481,134 @@ describe('EditOutputFlyout', () => { }); }); + describe('OpenTelemetry Exporter Configuration section', () => { + it('should show the OTel exporter configuration section for ES output', async () => { + const { utils } = renderFlyout({ + type: 'elasticsearch', + name: 'elasticsearch output', + id: 'output123', + is_default: false, + is_default_monitoring: false, + }); + + expect(utils.queryByText('OpenTelemetry Exporter Configuration')).not.toBeNull(); + expect(utils.queryByLabelText('Advanced Exporter Parameters')).not.toBeNull(); + }); + + it('should not show the OTel exporter configuration section for logstash output', async () => { + const { utils } = renderFlyout({ + type: 'logstash', + name: 'logstash output', + id: 'output123', + is_default: false, + is_default_monitoring: false, + }); + + expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); + expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + }); + + it('should not show the OTel exporter configuration section for kafka output', async () => { + const { utils } = renderFlyout({ + type: 'kafka', + name: 'kafka output', + id: 'output123', + is_default: false, + is_default_monitoring: false, + }); + + expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); + expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + }); + + it('should not show the OTel exporter configuration section for remote ES output', async () => { + jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(true); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ enableSyncIntegrationsOnRemote: true } as any); + + const { utils } = renderFlyout({ + type: 'remote_elasticsearch', + name: 'remote es output', + id: 'outputR', + is_default: false, + is_default_monitoring: false, + }); + + expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); + expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + }); + + it('should include otel_exporter_config_yaml in the save payload when creating an ES output', async () => { + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + mockedUseFleetStatus.mockReturnValue({ + isLoading: false, + isReady: true, + isSecretsStorageEnabled: true, + } as any); + + const { utils } = renderFlyout({ + type: 'elasticsearch', + name: 'elasticsearch output', + id: 'output123', + is_default: false, + is_default_monitoring: false, + hosts: ['http://localhost:9200'], + otel_exporter_config_yaml: 'flush_interval: 10s', + }); + + // Change a field so the Save button becomes enabled + fireEvent.change(utils.getByDisplayValue('elasticsearch output'), { + target: { value: 'updated output name' }, + }); + + fireEvent.click(utils.getByText('Save and apply settings')); + + await waitFor(() => { + expect(mockSendPutOutput).toHaveBeenCalledWith( + 'output123', + expect.objectContaining({ + otel_exporter_config_yaml: 'flush_interval: 10s', + }) + ); + }); + }); + + it('should send null otel_exporter_config_yaml when the field is empty', async () => { + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + mockedUseFleetStatus.mockReturnValue({ + isLoading: false, + isReady: true, + isSecretsStorageEnabled: true, + } as any); + + const { utils } = renderFlyout({ + type: 'elasticsearch', + name: 'elasticsearch output', + id: 'output123', + is_default: false, + is_default_monitoring: false, + hosts: ['http://localhost:9200'], + }); + + // Change a field so the Save button becomes enabled + fireEvent.change(utils.getByDisplayValue('elasticsearch output'), { + target: { value: 'updated output name' }, + }); + + fireEvent.click(utils.getByText('Save and apply settings')); + + await waitFor(() => { + expect(mockSendPutOutput).toHaveBeenCalledWith( + 'output123', + expect.objectContaining({ + otel_exporter_config_yaml: null, + }) + ); + }); + }); + }); + it('should not display remote ES output in type lists if serverless', async () => { jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); mockUseStartServices.mockReset(); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index df3521007be81..8e735697545a6 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -330,7 +330,6 @@ export const EditOutputFlyout: React.FunctionComponent = {renderOutputTypeSection(inputs.typeInput.value)} - {isRemoteESOutput ? null : ( = )} + + {isESOutput && ( + <> + + +

+ +

+
+ + + } + {...inputs.otelExporterConfigInput.formRowProps} + > + inputs.otelExporterConfigInput.setValue(value)} + disabled={inputs.otelExporterConfigInput.props.disabled} + placeholder={i18n.translate( + 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', + { + defaultMessage: + '# YAML settings here will be added to the exporter section of OTel policies.', + } + )} + /> + + + )} + ; presetInput: ReturnType; additionalYamlConfigInput: ReturnType; + otelExporterConfigInput: ReturnType; defaultOutputInput: ReturnType; defaultMonitoringOutputInput: ReturnType; caTrustedFingerprintInput: ReturnType; @@ -236,6 +237,12 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp isDisabled('config_yaml') ); + const otelExporterConfigInput = useInput( + (output as NewElasticsearchOutput)?.otel_exporter_config_yaml ?? '', + validateYamlConfig, + isDisabled('otel_exporter_config_yaml') + ); + const defaultOutputInput = useSwitchInput( output?.is_default ?? false, isDisabled('is_default') || output?.is_default @@ -604,6 +611,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp logstashHostsInput, presetInput, additionalYamlConfigInput, + otelExporterConfigInput, defaultOutputInput, defaultMonitoringOutputInput, caTrustedFingerprintInput, @@ -670,6 +678,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp const kafkaHeadersValid = kafkaHeadersInput.validate(); const logstashHostsValid = logstashHostsInput.validate(); const additionalYamlConfigValid = additionalYamlConfigInput.validate(); + const otelExporterConfigValid = otelExporterConfigInput.validate(); const caTrustedFingerprintValid = caTrustedFingerprintInput.validate(); const serviceTokenValid = serviceTokenInput.validate(); const serviceTokenSecretValid = serviceTokenSecretInput.validate(); @@ -738,6 +747,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp return ( elasticsearchUrlsValid && additionalYamlConfigValid && + otelExporterConfigValid && nameInputValid && caTrustedFingerprintValid && diskQueuePathValid @@ -757,6 +767,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp kafkaHeadersInput, logstashHostsInput, additionalYamlConfigInput, + otelExporterConfigInput, caTrustedFingerprintInput, serviceTokenInput, serviceTokenSecretInput, @@ -1034,6 +1045,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp is_default_monitoring: defaultMonitoringOutputInput.value, preset: presetInput.value, config_yaml: additionalYamlConfigInput.value, + otel_exporter_config_yaml: otelExporterConfigInput.value || null, ca_trusted_fingerprint: caTrustedFingerprintInput.value, proxy_id: proxyIdValue, write_to_logs_streams: writeToStreams.value, diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index c127c88443987..dcc35c0c363dd 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -595,6 +595,7 @@ export const getSavedObjectTypes = ( service_token: { type: 'keyword', index: false }, config: { type: 'flattened' }, config_yaml: { type: 'text' }, + otel_exporter_config_yaml: { type: 'text' }, is_preconfigured: { type: 'boolean', index: false }, is_internal: { type: 'boolean', index: false }, ssl: { type: 'binary' }, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index 488056ef9407f..1e4dc7dbc4c80 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -1387,4 +1387,60 @@ describe('generateOtelcolConfig', () => { expect(result.extensions?.['beatsauth/default']).toEqual({}); }); }); + + describe('otel_exporter_config_yaml merging', () => { + const inputs: FullAgentPolicyInput[] = [otelInput1]; + + it('should merge user YAML into the exporter config', () => { + const outputWithExporterYaml: Output = { + ...defaultOutput, + otel_exporter_config_yaml: 'flush_interval: 10s', + }; + + const result = generateOtelcolConfig(inputs, outputWithExporterYaml); + + expect(result.exporters?.['elasticsearch/default']).toEqual( + expect.objectContaining({ flush_interval: '10s' }) + ); + }); + + it('should not allow user YAML to override endpoints or auth', () => { + const outputWithOverrides: Output = { + ...defaultOutput, + otel_exporter_config_yaml: 'endpoints:\n - http://evil.com\nauth: null', + }; + + const result = generateOtelcolConfig(inputs, outputWithOverrides); + + expect(result.exporters?.['elasticsearch/default']).toEqual( + expect.objectContaining({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }) + ); + }); + + it('should handle null otel_exporter_config_yaml gracefully', () => { + const outputWithNull: Output = { + ...defaultOutput, + otel_exporter_config_yaml: null, + }; + + const result = generateOtelcolConfig(inputs, outputWithNull); + + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }); + }); + + it('should handle undefined otel_exporter_config_yaml gracefully', () => { + const result = generateOtelcolConfig(inputs, defaultOutput); + + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }); + }); + }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index 71c8d8f1932df..335fc060d7bb8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { load } from 'js-yaml'; + import type { FleetProxy, Output, TemplateAgentPolicyInput } from '../../types'; import type { FullAgentPolicyInput, @@ -459,12 +461,17 @@ function generateOtelcolExporter( case outputType.Elasticsearch: { const outputID = getOutputIdForAgentPolicy(dataOutput); const beatsauthID = `beatsauth/${outputID}`; + const extraExporterConfig = dataOutput.otel_exporter_config_yaml + ? (load(dataOutput.otel_exporter_config_yaml) as Record) ?? {} + : {}; return { extensions: { [beatsauthID]: buildBeatsauthConfig(dataOutput, proxy), }, exporters: { [`elasticsearch/${outputID}`]: { + ...extraExporterConfig, + // endpoints and auth always take precedence over user-supplied YAML endpoints: dataOutput.hosts, auth: { authenticator: beatsauthID }, }, diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 20ad16e8149ed..a3219c6e6465f 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -70,6 +70,7 @@ const BaseSchema = { ca_sha256: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), ca_trusted_fingerprint: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + otel_exporter_config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), ssl: schema.maybe( schema.oneOf([ schema.literal(null), diff --git a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts index 6d59ba7b91b07..b8cf9a050dcbc 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts @@ -181,6 +181,7 @@ export interface OutputSoBaseAttributes { is_internal?: boolean; is_preconfigured?: boolean; config_yaml?: string | null; + otel_exporter_config_yaml?: string | null; proxy_id?: string | null; shipper?: ShipperOutput | null; allow_edit?: string[]; From d113d4b23a738a2b531454980db88806b50ff160 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 25 Mar 2026 11:30:15 +0100 Subject: [PATCH 03/32] Improvements to UI --- .../components/edit_output_flyout/index.tsx | 85 ++++++++++--------- .../edit_output_flyout/use_output_form.tsx | 17 ++-- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 8e735697545a6..b1d72d3e6833e 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -32,6 +32,7 @@ import { EuiAccordion, EuiCode, useGeneratedHtmlId, + EuiPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -370,44 +371,6 @@ export const EditOutputFlyout: React.FunctionComponent = )} - {isESOutput && ( - <> - - -

- -

-
- - - } - {...inputs.otelExporterConfigInput.formRowProps} - > - inputs.otelExporterConfigInput.setValue(value)} - disabled={inputs.otelExporterConfigInput.props.disabled} - placeholder={i18n.translate( - 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', - { - defaultMessage: - '# YAML settings here will be added to the exporter section of OTel policies.', - } - )} - /> - - - )} - = )} + {isESOutput && ( + <> + + + +

+ +

+
+ + + } + helpText={ + + } + {...inputs.otelExporterConfigInput.formRowProps} + > + inputs.otelExporterConfigInput.setValue(value)} + disabled={inputs.otelExporterConfigInput.props.disabled} + placeholder={i18n.translate( + 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', + { + defaultMessage: + '# YAML settings here will be added to the exporter section of OTel policies.', + } + )} + /> + +
+ + )} + void, output?: Output, defaultOutp const allowEdit = output?.allow_edit ?? []; function isDisabled( - field: keyof Output | keyof KafkaOutput | keyof NewRemoteElasticsearchOutput + field: + | keyof Output + | keyof KafkaOutput + | keyof NewRemoteElasticsearchOutput + | keyof NewElasticsearchOutput ) { if (!authz.fleet.allSettings) { return true; @@ -1148,22 +1152,23 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOutp kafkaBrokerTimeoutInput.value, kafkaBrokerReachabilityTimeoutInput.value, kafkaBrokerAckReliabilityInput.value, - logstashEnableSSLInput.value, logstashHostsInput.value, + logstashEnableSSLInput.value, sslCertificateInput.value, sslKeyInput.value, sslCertificateAuthoritiesInput.value, sslKeySecretInput.value, - elasticsearchUrlInput.value, - presetInput.value, serviceTokenInput.value, serviceTokenSecretInput.value, + elasticsearchUrlInput.value, + presetInput.value, kibanaAPIKeyInput.value, syncIntegrationsInput.value, - syncUninstalledIntegrationsInput.value, kibanaURLInput.value, - caTrustedFingerprintInput.value, + syncUninstalledIntegrationsInput.value, writeToStreams.value, + otelExporterConfigInput.value, + caTrustedFingerprintInput.value, confirm, notifications.toasts, ]); From f802353a9f844b230cc3d6584c40ac4523710183 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 25 Mar 2026 16:12:59 +0100 Subject: [PATCH 04/32] update test --- .../edit_output_flyout/index.test.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index 36b87738a141b..e1d55323482cb 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -481,7 +481,7 @@ describe('EditOutputFlyout', () => { }); }); - describe('OpenTelemetry Exporter Configuration section', () => { + describe('OpenTelemetry Exporter section', () => { it('should show the OTel exporter configuration section for ES output', async () => { const { utils } = renderFlyout({ type: 'elasticsearch', @@ -491,11 +491,11 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter Configuration')).not.toBeNull(); - expect(utils.queryByLabelText('Advanced Exporter Parameters')).not.toBeNull(); + expect(utils.queryByText('OpenTelemetry Exporter')).not.toBeNull(); + expect(utils.queryByLabelText('Advanced YAML Configuration')).not.toBeNull(); }); - it('should not show the OTel exporter configuration section for logstash output', async () => { + it('should not show the OTel exporter section for logstash output', async () => { const { utils } = renderFlyout({ type: 'logstash', name: 'logstash output', @@ -504,11 +504,11 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); - expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); + expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); - it('should not show the OTel exporter configuration section for kafka output', async () => { + it('should not show the OTel exporter section for kafka output', async () => { const { utils } = renderFlyout({ type: 'kafka', name: 'kafka output', @@ -517,11 +517,11 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); - expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + expect(utils.queryByText('OpenTelemetry Exportern')).toBeNull(); + expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); - it('should not show the OTel exporter configuration section for remote ES output', async () => { + it('should not show the OTel exporter section for remote ES output', async () => { jest.spyOn(licenseService, 'isEnterprise').mockReturnValue(true); jest .spyOn(ExperimentalFeaturesService, 'get') @@ -535,8 +535,8 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter Configuration')).toBeNull(); - expect(utils.queryByLabelText('Advanced Exporter Parameters')).toBeNull(); + expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); + expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); it('should include otel_exporter_config_yaml in the save payload when creating an ES output', async () => { From 47b49c8c4e6ea62bf3f3ad163f770691bd08d88b Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 25 Mar 2026 17:03:58 +0100 Subject: [PATCH 05/32] Fix test and handle malformed yaml --- .../edit_output_flyout/index.test.tsx | 2 +- .../agent_policies/otel_collector.test.ts | 43 +++++++++++++++++++ .../services/agent_policies/otel_collector.ts | 19 ++++++-- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index e1d55323482cb..6779bb33cdfbc 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -517,7 +517,7 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exportern')).toBeNull(); + expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index 1e4dc7dbc4c80..c38175aca6c23 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -1442,5 +1442,48 @@ describe('generateOtelcolConfig', () => { auth: { authenticator: 'beatsauth/default' }, }); }); + + it('should handle malformed YAML without throwing', () => { + const outputWithBadYaml: Output = { + ...defaultOutput, + otel_exporter_config_yaml: ': invalid yaml', + }; + + expect(() => generateOtelcolConfig(inputs, outputWithBadYaml)).not.toThrow(); + + const result = generateOtelcolConfig(inputs, outputWithBadYaml); + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }); + }); + + it('should ignore non-object YAML (scalar)', () => { + const outputWithScalarYaml: Output = { + ...defaultOutput, + otel_exporter_config_yaml: 'just a string', + }; + + const result = generateOtelcolConfig(inputs, outputWithScalarYaml); + + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }); + }); + + it('should ignore non-object YAML (array)', () => { + const outputWithArrayYaml: Output = { + ...defaultOutput, + otel_exporter_config_yaml: '- a\n- b', + }; + + const result = generateOtelcolConfig(inputs, outputWithArrayYaml); + + expect(result.exporters?.['elasticsearch/default']).toEqual({ + endpoints: defaultOutput.hosts, + auth: { authenticator: 'beatsauth/default' }, + }); + }); }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index 335fc060d7bb8..28967e76d2163 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -450,6 +450,21 @@ function attachOtelcolExporter( return config; } +function parseOtelExporterConfigYaml(yaml: string | null | undefined): Record { + if (!yaml) return {}; + try { + const parsed = load(yaml); + if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) { + return parsed as Record; + } + return {}; + } catch { + // Malformed YAML — skip extra config rather than crashing policy generation. + // The UI validates YAML before saving; this path is only reachable via direct API writes. + return {}; + } +} + function generateOtelcolExporter( dataOutput: Output, proxy?: FleetProxy @@ -461,9 +476,7 @@ function generateOtelcolExporter( case outputType.Elasticsearch: { const outputID = getOutputIdForAgentPolicy(dataOutput); const beatsauthID = `beatsauth/${outputID}`; - const extraExporterConfig = dataOutput.otel_exporter_config_yaml - ? (load(dataOutput.otel_exporter_config_yaml) as Record) ?? {} - : {}; + const extraExporterConfig = parseOtelExporterConfigYaml(dataOutput.otel_exporter_config_yaml); return { extensions: { [beatsauthID]: buildBeatsauthConfig(dataOutput, proxy), From 94e84cde1bc122adf932292bada28cfbf7a4af10 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Thu, 26 Mar 2026 17:16:02 +0100 Subject: [PATCH 06/32] Address code review comments --- .../fleet/server/saved_objects/index.ts | 10 ++++ .../agent_policies/full_agent_policy.ts | 3 +- .../agent_policies/otel_collector.test.ts | 48 +++++++++++++++++++ .../services/agent_policies/otel_collector.ts | 44 +++++++++++++---- 4 files changed, 95 insertions(+), 10 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index dcc35c0c363dd..a00a40787c4c7 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -803,6 +803,16 @@ export const getSavedObjectTypes = ( }, ], }, + '9': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + otel_exporter_config_yaml: { type: 'text' }, + }, + }, + ], + }, }, migrations: { '7.13.0': migrateOutputToV7130, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts index 8d97ed64fb392..46ac134d19939 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts @@ -170,7 +170,8 @@ export async function getFullAgentPolicy( agentInputs, dataOutput, packageInfoCache, - dataOutputProxy + dataOutputProxy, + logger ); } diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index c38175aca6c23..8fc75821f762d 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -1386,6 +1386,54 @@ describe('generateOtelcolConfig', () => { expect(result.extensions?.['beatsauth/default']).toEqual({}); }); + + it('should always include beatsauth in extensions and exporter auth even when config is empty', () => { + // Verifies comment D: an empty beatsauth is a valid no-op that keeps the + // Beats-compatible transport layer active. The extension and auth.authenticator + // reference must always be present, regardless of SSL/proxy configuration. + const result = generateOtelcolConfig(inputs, defaultOutput); + + expect(result.extensions).toHaveProperty('beatsauth/default'); + expect(result.exporters?.['elasticsearch/default']).toEqual( + expect.objectContaining({ + auth: { authenticator: 'beatsauth/default' }, + }) + ); + expect(result.service?.extensions).toContain('beatsauth/default'); + }); + + it('should use secrets.ssl.key when present, ignoring plain ssl.key', () => { + const outputWithSecretKey: Output = { + ...defaultOutput, + ssl: { + key: 'plain-key-should-be-ignored', + }, + secrets: { + ssl: { key: { id: 'secret-id-abc123' } }, + }, + }; + + const result = generateOtelcolConfig(inputs, outputWithSecretKey); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + secrets: { ssl: { key: { id: 'secret-id-abc123' } } }, + }); + }); + + it('should include secrets.ssl.key in beatsauth when only secret key is set', () => { + const outputWithSecretOnly: Output = { + ...defaultOutput, + secrets: { + ssl: { key: { id: 'my-secret-id' } }, + }, + }; + + const result = generateOtelcolConfig(inputs, outputWithSecretOnly); + + expect(result.extensions?.['beatsauth/default']).toEqual({ + secrets: { ssl: { key: { id: 'my-secret-id' } } }, + }); + }); }); describe('otel_exporter_config_yaml merging', () => { diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index 28967e76d2163..edcdd2d608d39 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -7,6 +7,8 @@ import { load } from 'js-yaml'; +import type { Logger } from '@kbn/logging'; + import type { FleetProxy, Output, TemplateAgentPolicyInput } from '../../types'; import type { FullAgentPolicyInput, @@ -32,7 +34,8 @@ export function generateOtelcolConfig( inputs: FullAgentPolicyInput[] | TemplateAgentPolicyInput[], dataOutput?: Output, packageInfoCache?: Map, - proxy?: FleetProxy + proxy?: FleetProxy, + logger?: Logger ): OTelCollectorConfig { const otelConfigs: OTelCollectorConfig[] = inputs .filter((input) => input.type === OTEL_COLLECTOR_INPUT_TYPE) @@ -133,7 +136,7 @@ export function generateOtelcolConfig( } const config = mergeOtelcolConfigs(otelConfigs); - return attachOtelcolExporter(config, dataOutput, proxy); + return attachOtelcolExporter(config, dataOutput, proxy, logger); } function generateOtelTypeTransforms( @@ -386,10 +389,16 @@ function buildBeatsauthConfig(output: Output, proxy?: FleetProxy): Record 0) config.ssl = ssl; + // If the SSL key is stored as a Kibana secret, include it under the secrets namespace + if (output.secrets?.ssl?.key) { + config.secrets = { ssl: { key: output.secrets.ssl.key } }; + } + if (proxy) { config.proxy_url = proxy.url; if (proxy.proxy_headers) config.proxy_headers = proxy.proxy_headers; @@ -401,13 +410,14 @@ function buildBeatsauthConfig(output: Output, proxy?: FleetProxy): Record { +function parseOtelExporterConfigYaml( + yaml: string | null | undefined, + logger?: Logger +): Record { if (!yaml) return {}; try { const parsed = load(yaml); if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) { return parsed as Record; } + logger?.warn( + 'otel_exporter_config_yaml did not parse to an object, skipping extra exporter config' + ); return {}; - } catch { + } catch (e) { // Malformed YAML — skip extra config rather than crashing policy generation. // The UI validates YAML before saving; this path is only reachable via direct API writes. + logger?.warn( + `Failed to parse otel_exporter_config_yaml, skipping extra exporter config: ${e.message}` + ); return {}; } } function generateOtelcolExporter( dataOutput: Output, - proxy?: FleetProxy + proxy?: FleetProxy, + logger?: Logger ): { extensions: Record; exporters: Record; @@ -476,7 +496,13 @@ function generateOtelcolExporter( case outputType.Elasticsearch: { const outputID = getOutputIdForAgentPolicy(dataOutput); const beatsauthID = `beatsauth/${outputID}`; - const extraExporterConfig = parseOtelExporterConfigYaml(dataOutput.otel_exporter_config_yaml); + const extraExporterConfig = parseOtelExporterConfigYaml( + dataOutput.otel_exporter_config_yaml, + logger + ); + // beatsauth is always included, even when empty (no SSL/proxy configured). + // An empty beatsauth extension is a valid no-op that ensures the Beats-compatible + // HTTP transport layer is always active, consistent with the issue spec and Hybrid Agent behaviour. return { extensions: { [beatsauthID]: buildBeatsauthConfig(dataOutput, proxy), From 99235d126f148dc72b90bdb36f5a6651834673d8 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:35:57 +0000 Subject: [PATCH 07/32] Changes from node scripts/check_mappings_update --fix --- packages/kbn-check-saved-objects-cli/current_fields.json | 1 + .../kbn-check-saved-objects-cli/current_mappings.json | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/kbn-check-saved-objects-cli/current_fields.json b/packages/kbn-check-saved-objects-cli/current_fields.json index cf2dcc93f2b48..816b75f4edb4a 100644 --- a/packages/kbn-check-saved-objects-cli/current_fields.json +++ b/packages/kbn-check-saved-objects-cli/current_fields.json @@ -800,6 +800,7 @@ "is_preconfigured", "key", "name", + "otel_exporter_config_yaml", "output_id", "partition", "password", diff --git a/packages/kbn-check-saved-objects-cli/current_mappings.json b/packages/kbn-check-saved-objects-cli/current_mappings.json index da32549b0817f..0694da2d061f2 100644 --- a/packages/kbn-check-saved-objects-cli/current_mappings.json +++ b/packages/kbn-check-saved-objects-cli/current_mappings.json @@ -1492,6 +1492,9 @@ "install_version": { "type": "keyword" }, + "installed_as_dependency": { + "type": "boolean" + }, "installed_es": { "properties": { "deferred": { @@ -1535,9 +1538,6 @@ "enabled": false, "type": "object" }, - "installed_as_dependency": { - "type": "boolean" - }, "name": { "type": "keyword" }, @@ -2648,6 +2648,9 @@ "name": { "type": "keyword" }, + "otel_exporter_config_yaml": { + "type": "text" + }, "output_id": { "index": false, "type": "keyword" From 4d4e7ec88d6c92ac252b1dee8bf974a49bd96bea Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:00:16 +0000 Subject: [PATCH 08/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../saved_objects/check_registered_types.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 8e6de2742cece..9c0db317ecfe0 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "4f3451469b080548fd0f2ca414a81d91bd0d5690c34378376433ab1ae960ce5c", + "ingest-outputs": "1dac9681de5cd81e0e06863ba9d2bb84fc121ed5a8af3d1ecef787216b6a6fb9", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -844,8 +844,9 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-download-sources|10.1.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "================================================================================================", "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", - "ingest-outputs|mappings: a88cd423056155f954a3877a082c0f8d1273468a", + "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", + "ingest-outputs|10.9.0: 0603d7a8973b50da380d1982eceec7129cf3876780a116ecb05f508a4bd4e3a0", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", @@ -1470,7 +1471,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "10.0.0", "ingest-agent-policies": "10.9.0", "ingest-download-sources": "10.1.0", - "ingest-outputs": "10.8.0", + "ingest-outputs": "10.9.0", "ingest-package-policies": "10.22.0", "ingest_manager_settings": "10.8.0", "intercept_interaction_record": "10.1.0", @@ -1634,7 +1635,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "7.16.2", "ingest-agent-policies": "10.9.0", "ingest-download-sources": "10.1.0", - "ingest-outputs": "10.8.0", + "ingest-outputs": "10.9.0", "ingest-package-policies": "10.22.0", "ingest_manager_settings": "10.8.0", "intercept_interaction_record": "10.1.0", From 6131558f70193a17ee9015336784e5b7ffddce7b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 26 Mar 2026 17:17:10 +0000 Subject: [PATCH 09/32] Changes from make api-docs --- oas_docs/output/kibana.serverless.yaml | 36 ++++++++++++++++++++++++++ oas_docs/output/kibana.yaml | 36 ++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 077479685de3e..551531cb15213 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -43782,6 +43782,9 @@ paths: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -43929,6 +43932,9 @@ paths: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -44088,6 +44094,9 @@ paths: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -44288,6 +44297,9 @@ paths: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -68211,6 +68223,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -68428,6 +68443,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -68659,6 +68677,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -68803,6 +68824,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -68971,6 +68995,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -69188,6 +69215,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -69419,6 +69449,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -69563,6 +69596,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index b4c94a02d3958..b7b80515b51d0 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -46357,6 +46357,9 @@ paths: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -46504,6 +46507,9 @@ paths: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -46663,6 +46669,9 @@ paths: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -46863,6 +46872,9 @@ paths: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -78994,6 +79006,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -79211,6 +79226,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -79442,6 +79460,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -79586,6 +79607,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -79754,6 +79778,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced @@ -79971,6 +79998,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string partition: enum: - random @@ -80202,6 +80232,9 @@ components: type: boolean name: type: string + otel_exporter_config_yaml: + nullable: true + type: string proxy_id: nullable: true type: string @@ -80346,6 +80379,9 @@ components: type: string name: type: string + otel_exporter_config_yaml: + nullable: true + type: string preset: enum: - balanced From 019099a52e46260eef64419eaaca0f99c20c2e07 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Fri, 27 Mar 2026 09:59:55 +0100 Subject: [PATCH 10/32] Fix test and add accordion to UI --- .../ci_checks/check_registered_types.test.ts | 1 + .../components/edit_output_flyout/index.tsx | 102 +++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/x-pack/platform/plugins/shared/encrypted_saved_objects/integration_tests/ci_checks/check_registered_types.test.ts b/x-pack/platform/plugins/shared/encrypted_saved_objects/integration_tests/ci_checks/check_registered_types.test.ts index e7f77de3cb979..097b399c0efa1 100644 --- a/x-pack/platform/plugins/shared/encrypted_saved_objects/integration_tests/ci_checks/check_registered_types.test.ts +++ b/x-pack/platform/plugins/shared/encrypted_saved_objects/integration_tests/ci_checks/check_registered_types.test.ts @@ -139,6 +139,7 @@ describe('checking changes on all registered encrypted SO types', () => { "fleet-fleet-server-host|1", "fleet-uninstall-tokens|1", "ingest-download-sources|1", + "ingest-outputs|9", "ingest-outputs|8", "ingest-outputs|7", "ingest-outputs|6", diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 67ea48556203e..8b9fcfc48de30 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -548,54 +548,6 @@ export const EditOutputFlyout: React.FunctionComponent = )} - - {isESOutput && ( - <> - - - -

- -

-
- - - } - helpText={ - - } - {...inputs.otelExporterConfigInput.formRowProps} - > - inputs.otelExporterConfigInput.setValue(value)} - disabled={inputs.otelExporterConfigInput.props.disabled} - placeholder={i18n.translate( - 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', - { - defaultMessage: - '# YAML settings here will be added to the exporter section of OTel policies.', - } - )} - /> - -
- - )} - - @@ -627,6 +579,60 @@ export const EditOutputFlyout: React.FunctionComponent = /> + {isESOutput && ( + <> + + +

+ i + +

+ + } + paddingSize="none" + > + + + } + helpText={ + + } + {...inputs.otelExporterConfigInput.formRowProps} + > + inputs.otelExporterConfigInput.setValue(value)} + disabled={inputs.otelExporterConfigInput.props.disabled} + placeholder={i18n.translate( + 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', + { + defaultMessage: + '# YAML settings here will be added to the exporter section of OTel policies.', + } + )} + /> + + +
+ + )} + + {output?.id && output.type === 'remote_elasticsearch' ? ( From 91aab20d664776f657c59696a0ae19dfba6ddf46 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Fri, 27 Mar 2026 12:37:16 +0100 Subject: [PATCH 11/32] Address code review comments and fix tests --- .../components/edit_output_flyout/index.test.tsx | 13 ++++++++----- .../components/edit_output_flyout/index.tsx | 7 +++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index 6779bb33cdfbc..82aa3bdef78f7 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -491,8 +491,11 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter')).not.toBeNull(); - expect(utils.queryByLabelText('Advanced YAML Configuration')).not.toBeNull(); + expect(utils.queryByText('OpenTelemetry exporter')).not.toBeNull(); + + // Expand the accordion to reveal the YAML editor + fireEvent.click(utils.getByText('OpenTelemetry exporter')); + expect(utils.queryByLabelText('Advanced YAML configuration')).not.toBeNull(); }); it('should not show the OTel exporter section for logstash output', async () => { @@ -504,7 +507,7 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); + expect(utils.queryByText('OpenTelemetry exporter')).toBeNull(); expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); @@ -517,7 +520,7 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); + expect(utils.queryByText('OpenTelemetry exporter')).toBeNull(); expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); @@ -535,7 +538,7 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, }); - expect(utils.queryByText('OpenTelemetry Exporter')).toBeNull(); + expect(utils.queryByText('OpenTelemetry exporter')).toBeNull(); expect(utils.queryByLabelText('Advanced YAML Configuration')).toBeNull(); }); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 8b9fcfc48de30..69803a5e4c2ed 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -587,10 +587,9 @@ export const EditOutputFlyout: React.FunctionComponent = buttonContent={

- i

@@ -603,7 +602,7 @@ export const EditOutputFlyout: React.FunctionComponent = label={ } helpText={ @@ -622,7 +621,7 @@ export const EditOutputFlyout: React.FunctionComponent = 'xpack.fleet.settings.editOutputFlyout.otelExporterConfigPlaceholder', { defaultMessage: - '# YAML settings here will be added to the exporter section of OTel policies.', + '# YAML settings defined here will be added to the exporter section of OTel policies.', } )} /> From a138b68aac7d5c3b98e19e4d1996790d97fb3cda Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 30 Mar 2026 12:07:57 +0200 Subject: [PATCH 12/32] fix scout test --- .../cypress/e2e/fleet_settings_outputs.cy.ts | 8 +++-- .../components/edit_output_flyout/index.tsx | 36 ++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/cypress/e2e/fleet_settings_outputs.cy.ts b/x-pack/platform/plugins/shared/fleet/cypress/e2e/fleet_settings_outputs.cy.ts index 66801b8e692d0..dd34b996e411e 100644 --- a/x-pack/platform/plugins/shared/fleet/cypress/e2e/fleet_settings_outputs.cy.ts +++ b/x-pack/platform/plugins/shared/fleet/cypress/e2e/fleet_settings_outputs.cy.ts @@ -37,11 +37,15 @@ import { login } from '../tasks/login'; import { visit } from '../tasks/common'; export const fillYamlConfigBox = (query: string) => { - cy.get('[data-test-subj="kibanaCodeEditor"] textarea').type(query, { force: true }); + cy.get( + '[data-test-subj="settingsOutputsFlyout.yamlConfigInput"] [data-test-subj="kibanaCodeEditor"] textarea' + ).type(query, { force: true }); }; export const clearYamlConfigBox = () => { - cy.get('[data-test-subj="kibanaCodeEditor"] textarea').clear({ force: true }); + cy.get( + '[data-test-subj="settingsOutputsFlyout.yamlConfigInput"] [data-test-subj="kibanaCodeEditor"] textarea' + ).clear({ force: true }); }; describe('Outputs', () => { diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 69803a5e4c2ed..53f9056dee410 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -559,24 +559,26 @@ export const EditOutputFlyout: React.FunctionComponent = {...inputs.additionalYamlConfigInput.formRowProps} fullWidth > - { - if (outputYmlIncludesReservedPerformanceKey(value, load)) { - inputs.presetInput.setValue('custom'); - } +
+ { + if (outputYmlIncludesReservedPerformanceKey(value, load)) { + inputs.presetInput.setValue('custom'); + } - inputs.additionalYamlConfigInput.setValue(value); - }} - disabled={inputs.additionalYamlConfigInput.props.disabled} - placeholder={i18n.translate( - 'xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder', - { - defaultMessage: - '# YAML settings here will be added to the output section of each agent policy.', - } - )} - /> + inputs.additionalYamlConfigInput.setValue(value); + }} + disabled={inputs.additionalYamlConfigInput.props.disabled} + placeholder={i18n.translate( + 'xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder', + { + defaultMessage: + '# YAML settings here will be added to the output section of each agent policy.', + } + )} + /> +
{isESOutput && ( From 4451e00dd9c2832be5a9a36b1829a1de2d1984d2 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 30 Mar 2026 16:09:27 +0200 Subject: [PATCH 13/32] Address code review comments --- .../__fixtures__/ingest-outputs/10.9.0.json | 23 +++++++ .../fleet/server/saved_objects/index.ts | 5 ++ .../agent_policies/otel_collector.test.ts | 61 ++----------------- .../services/agent_policies/otel_collector.ts | 21 +++---- .../fleet/server/types/models/output.ts | 35 +++++++++++ 5 files changed, 79 insertions(+), 66 deletions(-) create mode 100644 packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json diff --git a/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json new file mode 100644 index 0000000000000..0acd15b338f4a --- /dev/null +++ b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json @@ -0,0 +1,23 @@ +{ + "10.8.0": [ + { + "name": "default", + "type": "elasticsearch", + "is_default": true, + "is_default_monitoring": true, + "hosts": ["https://localhost:9200"], + "preset": "balanced" + } + ], + "10.9.0": [ + { + "name": "default", + "type": "elasticsearch", + "is_default": true, + "is_default_monitoring": true, + "hosts": ["https://localhost:9200"], + "preset": "balanced", + "otel_exporter_config_yaml": null + } + ] +} diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index a00a40787c4c7..5a8baa1cb0e75 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -45,6 +45,7 @@ import { SettingsSchemaV7, SettingsSchemaV8, PackagePolicySchemaV22, + OutputSchemaV9, } from '../types'; import { migrateSyntheticsPackagePolicyToV8120 } from './migrations/synthetics/to_v8_12_0'; @@ -812,6 +813,10 @@ export const getSavedObjectTypes = ( }, }, ], + schemas: { + forwardCompatibility: OutputSchemaV9.extends({}, { unknowns: 'ignore' }), + create: OutputSchemaV9.extends({}, { unknowns: 'ignore' }), + }, }, }, migrations: { diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index 8fc75821f762d..9145f88259e0e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -255,9 +255,6 @@ describe('generateOtelcolConfig', () => { it('should return the otel config when there is one', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ - extensions: { - 'beatsauth/default': {}, - }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -290,11 +287,9 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/default' }, }, }, service: { - extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -313,9 +308,6 @@ describe('generateOtelcolConfig', () => { it('should use the output id when it is not the default', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; expect(generateOtelcolConfig(inputs, { ...defaultOutput, is_default: false })).toEqual({ - extensions: { - 'beatsauth/fleet-default-output': {}, - }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -348,11 +340,9 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/fleet-default-output': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/fleet-default-output' }, }, }, service: { - extensions: ['beatsauth/fleet-default-output'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -371,9 +361,6 @@ describe('generateOtelcolConfig', () => { it('should return the otel config if there is any', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ - extensions: { - 'beatsauth/default': {}, - }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -406,11 +393,9 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/default' }, }, }, service: { - extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -469,9 +454,6 @@ describe('generateOtelcolConfig', () => { it('should merge otel configs', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1, otelInput2]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ - extensions: { - 'beatsauth/default': {}, - }, receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -526,11 +508,9 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/default' }, }, }, service: { - extensions: ['beatsauth/default'], pipelines: { 'metrics/test-1-stream-id-1': { receivers: ['httpcheck/test-1-stream-id-1'], @@ -554,9 +534,6 @@ describe('generateOtelcolConfig', () => { it('should keep components with the same type', () => { const inputs: FullAgentPolicyInput[] = [otelInputMultipleComponentsSameType]; expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ - extensions: { - 'beatsauth/default': {}, - }, receivers: { 'httpcheck/1/test-3-stream-id-1': { targets: [ @@ -599,11 +576,9 @@ describe('generateOtelcolConfig', () => { exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/default' }, }, }, service: { - extensions: ['beatsauth/default'], pipelines: { 'metrics/test-3-stream-id-1': { receivers: ['httpcheck/1/test-3-stream-id-1', 'httpcheck/2/test-3-stream-id-1'], @@ -665,17 +640,12 @@ describe('generateOtelcolConfig', () => { 'elasticapm/apmtest': {}, forward: {}, }, - extensions: { - 'beatsauth/default': {}, - }, exporters: { 'elasticsearch/default': { endpoints: ['http://localhost:9200'], - auth: { authenticator: 'beatsauth/default' }, }, }, service: { - extensions: ['beatsauth/default'], pipelines: { 'traces/test-traces-stream-id-1': { receivers: ['zipkin/test-traces-stream-id-1'], @@ -1381,25 +1351,12 @@ describe('generateOtelcolConfig', () => { }); }); - it('should produce empty beatsauth config when output has no ssl or proxy fields', () => { - const result = generateOtelcolConfig(inputs, defaultOutput); - - expect(result.extensions?.['beatsauth/default']).toEqual({}); - }); - - it('should always include beatsauth in extensions and exporter auth even when config is empty', () => { - // Verifies comment D: an empty beatsauth is a valid no-op that keeps the - // Beats-compatible transport layer active. The extension and auth.authenticator - // reference must always be present, regardless of SSL/proxy configuration. + it('should omit beatsauth from extensions and exporter auth when output has no ssl or proxy fields', () => { const result = generateOtelcolConfig(inputs, defaultOutput); - expect(result.extensions).toHaveProperty('beatsauth/default'); - expect(result.exporters?.['elasticsearch/default']).toEqual( - expect.objectContaining({ - auth: { authenticator: 'beatsauth/default' }, - }) - ); - expect(result.service?.extensions).toContain('beatsauth/default'); + expect(result.extensions?.['beatsauth/default']).toBeUndefined(); + expect(result.exporters?.['elasticsearch/default']).not.toHaveProperty('auth'); + expect(result.service?.extensions ?? []).not.toContain('beatsauth/default'); }); it('should use secrets.ssl.key when present, ignoring plain ssl.key', () => { @@ -1452,10 +1409,10 @@ describe('generateOtelcolConfig', () => { ); }); - it('should not allow user YAML to override endpoints or auth', () => { + it('should not allow user YAML to override endpoints', () => { const outputWithOverrides: Output = { ...defaultOutput, - otel_exporter_config_yaml: 'endpoints:\n - http://evil.com\nauth: null', + otel_exporter_config_yaml: 'endpoints:\n - http://evil.com', }; const result = generateOtelcolConfig(inputs, outputWithOverrides); @@ -1463,7 +1420,6 @@ describe('generateOtelcolConfig', () => { expect(result.exporters?.['elasticsearch/default']).toEqual( expect.objectContaining({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }) ); }); @@ -1478,7 +1434,6 @@ describe('generateOtelcolConfig', () => { expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }); }); @@ -1487,7 +1442,6 @@ describe('generateOtelcolConfig', () => { expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }); }); @@ -1502,7 +1456,6 @@ describe('generateOtelcolConfig', () => { const result = generateOtelcolConfig(inputs, outputWithBadYaml); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }); }); @@ -1516,7 +1469,6 @@ describe('generateOtelcolConfig', () => { expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }); }); @@ -1530,7 +1482,6 @@ describe('generateOtelcolConfig', () => { expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, - auth: { authenticator: 'beatsauth/default' }, }); }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index edcdd2d608d39..2cffcb41d5547 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -422,10 +422,12 @@ function attachOtelcolExporter( ...config.connectors, forward: {}, }; - config.extensions = { - ...config.extensions, - ...extensions, - }; + if (Object.keys(extensions).length > 0) { + config.extensions = { + ...config.extensions, + ...extensions, + }; + } config.exporters = { ...config.exporters, ...exporters, @@ -495,24 +497,21 @@ function generateOtelcolExporter( switch (dataOutput.type) { case outputType.Elasticsearch: { const outputID = getOutputIdForAgentPolicy(dataOutput); + const beatsauthConfig = buildBeatsauthConfig(dataOutput, proxy); + const hasBeatsauthConfig = Object.keys(beatsauthConfig).length > 0; const beatsauthID = `beatsauth/${outputID}`; const extraExporterConfig = parseOtelExporterConfigYaml( dataOutput.otel_exporter_config_yaml, logger ); - // beatsauth is always included, even when empty (no SSL/proxy configured). - // An empty beatsauth extension is a valid no-op that ensures the Beats-compatible - // HTTP transport layer is always active, consistent with the issue spec and Hybrid Agent behaviour. return { - extensions: { - [beatsauthID]: buildBeatsauthConfig(dataOutput, proxy), - }, + extensions: hasBeatsauthConfig ? { [beatsauthID]: beatsauthConfig } : {}, exporters: { [`elasticsearch/${outputID}`]: { ...extraExporterConfig, // endpoints and auth always take precedence over user-supplied YAML endpoints: dataOutput.hosts, - auth: { authenticator: beatsauthID }, + ...(hasBeatsauthConfig ? { auth: { authenticator: beatsauthID } } : {}), }, }, }; diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 3781a0fb6a156..d089c03d8fbf4 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -336,6 +336,41 @@ const KafkaUpdateSchema = { ), }; +/** + * Versioned schemas for SO model versions (used by the check_saved_objects CLI) + */ + +// Schema representing the ingest-outputs SO shape at model version 9 (adds otel_exporter_config_yaml) +export const OutputSchemaV9 = schema.object({ + ...BaseSchema, + type: schema.string(), + // ES output fields + hosts: schema.maybe(schema.arrayOf(schema.string())), + preset: schema.maybe(schema.string()), + // Remote ES output fields + service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + // Logstash output fields + // Kafka output fields + version: schema.maybe(schema.string()), + compression: schema.maybe(schema.string()), + timeout: schema.maybe(schema.number()), + broker_timeout: schema.maybe(schema.number()), + required_acks: schema.maybe(schema.number()), + client_id: schema.maybe(schema.string()), + username: schema.maybe(schema.string()), + password: schema.maybe(schema.string()), + partition: schema.maybe(schema.string()), + topic: schema.maybe(schema.string()), + topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), + round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), + random: schema.maybe(schema.object({}, { unknowns: 'allow' })), + sasl: schema.maybe(schema.object({}, { unknowns: 'allow' })), + auth_type: schema.maybe(schema.string()), + connection_type: schema.maybe(schema.string()), +}); + export const OutputSchema = schema.oneOf([ schema.object({ ...ElasticSearchSchema }, { meta: { id: 'output_elasticsearch' } }), schema.object({ ...RemoteElasticSearchSchema }, { meta: { id: 'output_remote_elasticsearch' } }), From 36c5cec0a60144f53ddc82baf832c6029918a0e4 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:39:00 +0000 Subject: [PATCH 14/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index e065b5e32e994..0b8f2d2d73f4a 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "1dac9681de5cd81e0e06863ba9d2bb84fc121ed5a8af3d1ecef787216b6a6fb9", + "ingest-outputs": "21bb3755cc285e69c6b9be04a6e4cf9ba122233714ceafbef4c6efd33883efae", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: 0603d7a8973b50da380d1982eceec7129cf3876780a116ecb05f508a4bd4e3a0", + "ingest-outputs|10.9.0: f908f9dceb743961198f7589838543b0df0b6833fa855b48571feb8978680f4f", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From 88a5268ce0dd2f85ff505a91064df5095f4fa750 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 30 Mar 2026 17:01:27 +0200 Subject: [PATCH 15/32] Code review part II --- .../fleet/server/saved_objects/index.ts | 6 +- .../fleet/server/types/models/output.ts | 94 +++++++++++++++++-- .../fleet/server/types/rest_spec/settings.ts | 1 + 3 files changed, 90 insertions(+), 11 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index 5a8baa1cb0e75..34e5243ddda02 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -45,7 +45,7 @@ import { SettingsSchemaV7, SettingsSchemaV8, PackagePolicySchemaV22, - OutputSchemaV9, + OutputSOForwardCompatSchemaV9, } from '../types'; import { migrateSyntheticsPackagePolicyToV8120 } from './migrations/synthetics/to_v8_12_0'; @@ -814,8 +814,8 @@ export const getSavedObjectTypes = ( }, ], schemas: { - forwardCompatibility: OutputSchemaV9.extends({}, { unknowns: 'ignore' }), - create: OutputSchemaV9.extends({}, { unknowns: 'ignore' }), + forwardCompatibility: OutputSOForwardCompatSchemaV9.extends({}, { unknowns: 'ignore' }), + create: OutputSOForwardCompatSchemaV9.extends({}, { unknowns: 'ignore' }), }, }, }, diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index d089c03d8fbf4..f2d6a980bbb8b 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -338,19 +338,53 @@ const KafkaUpdateSchema = { /** * Versioned schemas for SO model versions (used by the check_saved_objects CLI) + * + * Note: these schemas reflect the SO storage shape, not the API shape. + * In particular, `ssl` is stored as a JSON-serialised string (binary mapping), + * `secrets` uses `{ id: string }` references, and all fields that are absent in + * a given output type must be marked optional. */ -// Schema representing the ingest-outputs SO shape at model version 9 (adds otel_exporter_config_yaml) -export const OutputSchemaV9 = schema.object({ - ...BaseSchema, - type: schema.string(), - // ES output fields +// Base SO schema fields shared across all output types at model version 9. +// `ssl` is stored as a JSON string (binary), so we accept a plain string here. +const BaseSOSchemaV9 = { + id: schema.maybe(schema.string()), + name: schema.string(), + is_default: schema.boolean({ defaultValue: false }), + is_default_monitoring: schema.boolean({ defaultValue: false }), + is_internal: schema.maybe(schema.boolean()), + is_preconfigured: schema.maybe(schema.boolean()), + ca_sha256: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + ca_trusted_fingerprint: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + otel_exporter_config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + // ssl is stored as a JSON-serialised string in the SO (binary ES mapping) + ssl: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + proxy_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + shipper: schema.maybe(schema.object({}, { unknowns: 'allow' })), + allow_edit: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 1000 })), + secrets: schema.maybe(schema.object({}, { unknowns: 'allow' })), +}; + +// Schema representing the ingest-outputs ES SO shape at model version 9 +export const ElasticSearchSchemaV9 = schema.object({ + ...BaseSOSchemaV9, + type: schema.literal(outputType.Elasticsearch), + hosts: schema.maybe(schema.arrayOf(schema.string())), + preset: schema.maybe(schema.string()), + write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), +}); + +// Loose schema used for SO forwardCompatibility at model version 9. +// Must be a plain schema.object() (not oneOf) so that .extends({}, { unknowns: 'ignore' }) works. +// It accepts all fields that any output type may have, making all type-specific fields optional. +export const OutputSOForwardCompatSchemaV9 = schema.object({ + ...BaseSOSchemaV9, + type: schema.maybe(schema.string()), hosts: schema.maybe(schema.arrayOf(schema.string())), preset: schema.maybe(schema.string()), - // Remote ES output fields + write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - // Logstash output fields - // Kafka output fields version: schema.maybe(schema.string()), compression: schema.maybe(schema.string()), timeout: schema.maybe(schema.number()), @@ -371,6 +405,50 @@ export const OutputSchemaV9 = schema.object({ connection_type: schema.maybe(schema.string()), }); +// Schema representing the ingest-outputs SO shape at model version 9 (adds otel_exporter_config_yaml) +export const OutputSchemaV9 = schema.oneOf([ + ElasticSearchSchemaV9, + // Remote Elasticsearch + schema.object({ + ...BaseSOSchemaV9, + type: schema.literal(outputType.RemoteElasticsearch), + hosts: schema.maybe(schema.arrayOf(schema.string())), + preset: schema.maybe(schema.string()), + service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), + }), + // Logstash + schema.object({ + ...BaseSOSchemaV9, + type: schema.literal(outputType.Logstash), + hosts: schema.maybe(schema.arrayOf(schema.string())), + }), + // Kafka + schema.object({ + ...BaseSOSchemaV9, + type: schema.literal(outputType.Kafka), + hosts: schema.maybe(schema.arrayOf(schema.string())), + version: schema.maybe(schema.string()), + compression: schema.maybe(schema.string()), + timeout: schema.maybe(schema.number()), + broker_timeout: schema.maybe(schema.number()), + required_acks: schema.maybe(schema.number()), + client_id: schema.maybe(schema.string()), + username: schema.maybe(schema.string()), + password: schema.maybe(schema.string()), + partition: schema.maybe(schema.string()), + topic: schema.maybe(schema.string()), + topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), + round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), + random: schema.maybe(schema.object({}, { unknowns: 'allow' })), + sasl: schema.maybe(schema.object({}, { unknowns: 'allow' })), + auth_type: schema.maybe(schema.string()), + connection_type: schema.maybe(schema.string()), + }), +]); + export const OutputSchema = schema.oneOf([ schema.object({ ...ElasticSearchSchema }, { meta: { id: 'output_elasticsearch' } }), schema.object({ ...RemoteElasticSearchSchema }, { meta: { id: 'output_remote_elasticsearch' } }), diff --git a/x-pack/platform/plugins/shared/fleet/server/types/rest_spec/settings.ts b/x-pack/platform/plugins/shared/fleet/server/types/rest_spec/settings.ts index 1bc279689a737..029d907c215c5 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/rest_spec/settings.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/rest_spec/settings.ts @@ -53,6 +53,7 @@ const EnrollmentSettingsOutputSchema = schema.object({ ca_sha256: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), ca_trusted_fingerprint: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + otel_exporter_config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), proxy_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), allow_edit: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 100 })), preset: schema.maybe( From 034a2404cffb2f65e6df017d55b888b24e51a00a Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Mon, 30 Mar 2026 17:11:39 +0200 Subject: [PATCH 16/32] add maxSize in schemas --- .../fleet/server/types/models/output.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index f2d6a980bbb8b..be69d313b3773 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -370,7 +370,7 @@ const BaseSOSchemaV9 = { export const ElasticSearchSchemaV9 = schema.object({ ...BaseSOSchemaV9, type: schema.literal(outputType.Elasticsearch), - hosts: schema.maybe(schema.arrayOf(schema.string())), + hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), preset: schema.maybe(schema.string()), write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), }); @@ -381,7 +381,7 @@ export const ElasticSearchSchemaV9 = schema.object({ export const OutputSOForwardCompatSchemaV9 = schema.object({ ...BaseSOSchemaV9, type: schema.maybe(schema.string()), - hosts: schema.maybe(schema.arrayOf(schema.string())), + hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), preset: schema.maybe(schema.string()), write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), @@ -395,8 +395,8 @@ export const OutputSOForwardCompatSchemaV9 = schema.object({ password: schema.maybe(schema.string()), partition: schema.maybe(schema.string()), topic: schema.maybe(schema.string()), - topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), - headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 })), + headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 })), hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), random: schema.maybe(schema.object({}, { unknowns: 'allow' })), @@ -412,7 +412,7 @@ export const OutputSchemaV9 = schema.oneOf([ schema.object({ ...BaseSOSchemaV9, type: schema.literal(outputType.RemoteElasticsearch), - hosts: schema.maybe(schema.arrayOf(schema.string())), + hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), preset: schema.maybe(schema.string()), service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), @@ -421,13 +421,13 @@ export const OutputSchemaV9 = schema.oneOf([ schema.object({ ...BaseSOSchemaV9, type: schema.literal(outputType.Logstash), - hosts: schema.maybe(schema.arrayOf(schema.string())), + hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), }), // Kafka schema.object({ ...BaseSOSchemaV9, type: schema.literal(outputType.Kafka), - hosts: schema.maybe(schema.arrayOf(schema.string())), + hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), version: schema.maybe(schema.string()), compression: schema.maybe(schema.string()), timeout: schema.maybe(schema.number()), @@ -438,8 +438,12 @@ export const OutputSchemaV9 = schema.oneOf([ password: schema.maybe(schema.string()), partition: schema.maybe(schema.string()), topic: schema.maybe(schema.string()), - topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), - headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + topics: schema.maybe( + schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }) + ), + headers: schema.maybe( + schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }) + ), hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), random: schema.maybe(schema.object({}, { unknowns: 'allow' })), From 0562caab45f08d0877a3ec52c5b187ee0be1a7fe Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:41:44 +0000 Subject: [PATCH 17/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 0b8f2d2d73f4a..14280dea4044b 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "21bb3755cc285e69c6b9be04a6e4cf9ba122233714ceafbef4c6efd33883efae", + "ingest-outputs": "8f69da62e7da3561aac199a27ad0bf95f7e095903de613b7696f0d703afbf384", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: f908f9dceb743961198f7589838543b0df0b6833fa855b48571feb8978680f4f", + "ingest-outputs|10.9.0: 77b915fc7efa36bae77dec172626543a9b13a3068e6e4690ee1f0788dbd3ed19", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From c040320414a837db0c2912f89ebfe2908d5a2407 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 31 Mar 2026 10:13:34 +0200 Subject: [PATCH 18/32] Add missing compression_level --- .../platform/plugins/shared/fleet/server/types/models/output.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index be69d313b3773..fc8c40238bdc2 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -403,6 +403,7 @@ export const OutputSOForwardCompatSchemaV9 = schema.object({ sasl: schema.maybe(schema.object({}, { unknowns: 'allow' })), auth_type: schema.maybe(schema.string()), connection_type: schema.maybe(schema.string()), + compression_level: schema.maybe(schema.number()), }); // Schema representing the ingest-outputs SO shape at model version 9 (adds otel_exporter_config_yaml) @@ -430,6 +431,7 @@ export const OutputSchemaV9 = schema.oneOf([ hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), version: schema.maybe(schema.string()), compression: schema.maybe(schema.string()), + compression_level: schema.maybe(schema.number()), timeout: schema.maybe(schema.number()), broker_timeout: schema.maybe(schema.number()), required_acks: schema.maybe(schema.number()), From 0a61371caf8a767d8ad223a0aca3fc6a2e386736 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 31 Mar 2026 10:40:16 +0200 Subject: [PATCH 19/32] Refactor generateOtelcolConfig --- .../agent_policies/full_agent_policy.test.ts | 25 ++-- .../agent_policies/full_agent_policy.ts | 10 +- .../agent_policies/otel_collector.test.ts | 123 ++++++++++-------- .../services/agent_policies/otel_collector.ts | 23 ++-- .../epm/packages/get_template_inputs.ts | 10 +- 5 files changed, 105 insertions(+), 86 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts index 8b5c351babb6c..dc358d672395c 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -1832,11 +1832,10 @@ describe('getFullAgentPolicy', () => { await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); - const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0][0]; expect(callArgs).toBeDefined(); - // Third argument should be the packageInfoCache Map - expect(callArgs[2]).toBeInstanceOf(Map); - const packageInfoCache = callArgs[2] as Map; + expect(callArgs.packageInfoCache).toBeInstanceOf(Map); + const packageInfoCache = callArgs.packageInfoCache as Map; expect(packageInfoCache.has('otelpackage-1.0.0')).toBe(true); expect(packageInfoCache.get('otelpackage-1.0.0')).toEqual(packageInfo); }); @@ -2023,9 +2022,9 @@ describe('getFullAgentPolicy', () => { await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); - const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; - // Fourth argument should be the resolved proxy - expect(callArgs[3]).toEqual(proxy); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0][0]; + // proxy should be the resolved proxy + expect(callArgs.proxy).toEqual(proxy); }); it('should pass undefined proxy to generateOtelcolConfig when dataOutput has no proxy_id', async () => { @@ -2034,9 +2033,9 @@ describe('getFullAgentPolicy', () => { await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); - const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; - // Fourth argument should be undefined when no proxy_id - expect(callArgs[3]).toBeUndefined(); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0][0]; + // proxy should be undefined when no proxy_id + expect(callArgs.proxy).toBeUndefined(); }); it('should pass undefined proxy to generateOtelcolConfig when proxy_id does not match any proxy', async () => { @@ -2091,9 +2090,9 @@ describe('getFullAgentPolicy', () => { await getFullAgentPolicy(createSavedObjectClientMock(), 'agent-policy'); expect(mockedGenerateOtelcolConfig).toHaveBeenCalled(); - const callArgs = mockedGenerateOtelcolConfig.mock.calls[0]; - // Fourth argument should be undefined when proxy_id doesn't match - expect(callArgs[3]).toBeUndefined(); + const callArgs = mockedGenerateOtelcolConfig.mock.calls[0][0]; + // proxy should be undefined when proxy_id doesn't match + expect(callArgs.proxy).toBeUndefined(); }); }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts index 46ac134d19939..5d01ac4abf678 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts @@ -166,13 +166,13 @@ export async function getFullAgentPolicy( const dataOutputProxy = dataOutput?.proxy_id ? proxies.find((p) => p.id === dataOutput.proxy_id) : undefined; - otelcolConfig = generateOtelcolConfig( - agentInputs, + otelcolConfig = generateOtelcolConfig({ + inputs: agentInputs, dataOutput, packageInfoCache, - dataOutputProxy, - logger - ); + proxy: dataOutputProxy, + logger, + }); } const inputs = agentInputs diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts index 06f45eca703ab..dbf0bd89a7c99 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.test.ts @@ -244,17 +244,17 @@ describe('generateOtelcolConfig', () => { it('should be empty if there is no input', () => { const inputs: FullAgentPolicyInput[] = []; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({}); + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({}); }); it('should be empty if there is no otel config', () => { const inputs: FullAgentPolicyInput[] = [logInput]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({}); + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({}); }); it('should return the otel config when there is one', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({ receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -307,7 +307,9 @@ describe('generateOtelcolConfig', () => { it('should use the output id when it is not the default', () => { const inputs: FullAgentPolicyInput[] = [otelInput1]; - expect(generateOtelcolConfig(inputs, { ...defaultOutput, is_default: false })).toEqual({ + expect( + generateOtelcolConfig({ inputs, dataOutput: { ...defaultOutput, is_default: false } }) + ).toEqual({ receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -360,7 +362,7 @@ describe('generateOtelcolConfig', () => { it('should return the otel config if there is any', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({ receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -413,7 +415,7 @@ describe('generateOtelcolConfig', () => { it('should also work for templates', () => { const inputs: TemplateAgentPolicyInput[] = [otelInputTemplate]; - expect(generateOtelcolConfig(inputs)).toEqual({ + expect(generateOtelcolConfig({ inputs })).toEqual({ receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -453,7 +455,7 @@ describe('generateOtelcolConfig', () => { it('should merge otel configs', () => { const inputs: FullAgentPolicyInput[] = [logInput, otelInput1, otelInput2]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({ receivers: { 'httpcheck/test-1-stream-id-1': { targets: [ @@ -533,7 +535,7 @@ describe('generateOtelcolConfig', () => { it('should keep components with the same type', () => { const inputs: FullAgentPolicyInput[] = [otelInputMultipleComponentsSameType]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({ receivers: { 'httpcheck/1/test-3-stream-id-1': { targets: [ @@ -600,7 +602,7 @@ describe('generateOtelcolConfig', () => { it('should add elasticapm connector and processor for traces input with use_apm enabled', () => { const inputs: FullAgentPolicyInput[] = [otelTracesInputWithAPM]; - expect(generateOtelcolConfig(inputs, defaultOutput)).toEqual({ + expect(generateOtelcolConfig({ inputs, dataOutput: defaultOutput })).toEqual({ receivers: { 'zipkin/test-traces-stream-id-1': { endpoint: 'localhost:9411', @@ -704,7 +706,7 @@ describe('generateOtelcolConfig', () => { ], }; - const result = generateOtelcolConfig([inputA, inputB], defaultOutput); + const result = generateOtelcolConfig({ inputs: [inputA, inputB], dataOutput: defaultOutput }); expect(result.connectors?.['elasticapm/ns-a']).toEqual({}); expect(result.connectors?.['elasticapm/ns-b']).toEqual({}); @@ -781,7 +783,7 @@ describe('generateOtelcolConfig', () => { ], }; - const result = generateOtelcolConfig([inputA, inputB], defaultOutput); + const result = generateOtelcolConfig({ inputs: [inputA, inputB], dataOutput: defaultOutput }); expect(result.connectors?.['elasticapm/ns-shared']).toEqual({}); expect(result.connectors?.['elasticapm/policy-a-stream-id-1']).toBeUndefined(); @@ -941,7 +943,7 @@ describe('generateOtelcolConfig', () => { })) ?? [], }; const inputs: FullAgentPolicyInput[] = [inputWithUseApm]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCache); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, packageInfoCache }); expect(result.connectors?.['elasticapm/default']).toEqual({}); expect(result.processors?.['elasticapm/default']).toEqual({}); @@ -964,7 +966,7 @@ describe('generateOtelcolConfig', () => { it('should generate transform with multiple signal type statements when dynamic_signal_types is true', () => { const inputs: FullAgentPolicyInput[] = [otelInputWithMultipleSignalTypes]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCache); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, packageInfoCache }); expect(result.processors?.['transform/test-multi-signal-stream-id-1-routing']).toEqual({ log_statements: [ @@ -1019,7 +1021,7 @@ describe('generateOtelcolConfig', () => { it('should generate transform with multiple signal type statements when dynamic_signal_types is true and pipelines have simple names', () => { const inputs: FullAgentPolicyInput[] = [otelInputWithMultipleSignalTypes2]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCache); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, packageInfoCache }); expect(result.processors?.['transform/test-multi-signal-stream-id-1-routing']).toEqual({ log_statements: [ @@ -1098,7 +1100,7 @@ describe('generateOtelcolConfig', () => { }; const inputs: FullAgentPolicyInput[] = [otelInputWithSubsetSignalTypes]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCache); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, packageInfoCache }); expect(result.processors?.['transform/test-multi-signal-stream-id-1-routing']).toEqual({ log_statements: [ @@ -1151,7 +1153,11 @@ describe('generateOtelcolConfig', () => { ]); const inputs: FullAgentPolicyInput[] = [otelInputWithMultipleSignalTypes]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCacheNoDynamic); + const result = generateOtelcolConfig({ + inputs, + dataOutput: defaultOutput, + packageInfoCache: packageInfoCacheNoDynamic, + }); // Should generate single signal type transform (uses stream.data_stream.type) expect(result.processors?.['transform/test-multi-signal-stream-id-1-routing']).toEqual({ @@ -1213,7 +1219,11 @@ describe('generateOtelcolConfig', () => { ]); const inputs: FullAgentPolicyInput[] = [otelInputWithMetricsType]; - const result = generateOtelcolConfig(inputs, defaultOutput, packageInfoCacheNoDynamicVar); + const result = generateOtelcolConfig({ + inputs, + dataOutput: defaultOutput, + packageInfoCache: packageInfoCacheNoDynamicVar, + }); // Should use the stream's data_stream.type (metrics) expect(result.processors?.['transform/test-multi-signal-stream-id-1-routing']).toEqual({ @@ -1261,7 +1271,10 @@ describe('generateOtelcolConfig', () => { }; it('generates OTel config for integration package with otelcol input', () => { - const result = generateOtelcolConfig([otelIntegrationInput], defaultOutput); + const result = generateOtelcolConfig({ + inputs: [otelIntegrationInput], + dataOutput: defaultOutput, + }); expect(result.receivers).toHaveProperty('otlp/integration-otel-stream-id-1'); expect(result.exporters).toHaveProperty('elasticsearch/default'); @@ -1312,14 +1325,10 @@ describe('generateOtelcolConfig', () => { ], } as any; - const result = generateOtelcolConfig( - [templateInput], - undefined, - undefined, - undefined, - undefined, - integrationPackageInfo - ); + const result = generateOtelcolConfig({ + inputs: [templateInput], + defaultPackageInfo: integrationPackageInfo, + }); // With dynamic_signal_types, routing transforms are generated per signal type const routingKey = Object.keys(result.processors ?? {}).find((k) => @@ -1367,12 +1376,10 @@ describe('generateOtelcolConfig', () => { ], } as any; - const result = generateOtelcolConfig( - [templateInput], - undefined, - undefined, - integrationPackageInfo - ); + const result = generateOtelcolConfig({ + inputs: [templateInput], + defaultPackageInfo: integrationPackageInfo, + }); const routingKey = Object.keys(result.processors ?? {}).find((k) => k.startsWith('transform/') @@ -1447,12 +1454,11 @@ describe('generateOtelcolConfig', () => { ], }; - const result = generateOtelcolConfig( - [inputWithMeta], - undefined, - packageInfoCacheWithDynamic, - defaultPkgInfoNoDynamic - ); + const result = generateOtelcolConfig({ + inputs: [inputWithMeta], + packageInfoCache: packageInfoCacheWithDynamic, + defaultPackageInfo: defaultPkgInfoNoDynamic, + }); const routingKey = Object.keys(result.processors ?? {}).find((k) => k.startsWith('transform/') @@ -1530,7 +1536,7 @@ describe('generateOtelcolConfig', () => { }; const cache = new Map([['mixed_pkg-1.0.0', mixedPackageInfo]]); - const result = generateOtelcolConfig([nonDynamicInput], undefined, cache); + const result = generateOtelcolConfig({ inputs: [nonDynamicInput], packageInfoCache: cache }); const routingKey = Object.keys(result.processors ?? {}).find((k) => k.startsWith('transform/') @@ -1607,7 +1613,10 @@ describe('generateOtelcolConfig', () => { }; const cache = new Map([['combined_pkg-1.0.0', mixedInputsPackageInfo]]); - const result = generateOtelcolConfig([nonDynamicOtelInput], undefined, cache); + const result = generateOtelcolConfig({ + inputs: [nonDynamicOtelInput], + packageInfoCache: cache, + }); const routingKey = Object.keys(result.processors ?? {}).find((k) => k.startsWith('transform/') @@ -1636,7 +1645,7 @@ describe('generateOtelcolConfig', () => { }, }; - const result = generateOtelcolConfig(inputs, outputWithSSL); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithSSL }); expect(result.extensions?.['beatsauth/default']).toEqual({ ssl: { @@ -1660,7 +1669,7 @@ describe('generateOtelcolConfig', () => { ca_trusted_fingerprint: 'myfingerprint', }; - const result = generateOtelcolConfig(inputs, outputWithFingerprint); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithFingerprint }); expect(result.extensions?.['beatsauth/default']).toEqual({ ssl: { ca_trusted_fingerprint: 'myfingerprint' }, @@ -1673,7 +1682,7 @@ describe('generateOtelcolConfig', () => { ca_sha256: 'sha256value', }; - const result = generateOtelcolConfig(inputs, outputWithCaSha); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithCaSha }); expect(result.extensions?.['beatsauth/default']).toEqual({ ssl: { ca_sha256: 'sha256value' }, @@ -1689,7 +1698,7 @@ describe('generateOtelcolConfig', () => { is_preconfigured: false, }; - const result = generateOtelcolConfig(inputs, defaultOutput, undefined, proxy); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, proxy }); expect(result.extensions?.['beatsauth/default']).toEqual({ proxy_url: 'http://proxy.example.com:3128', @@ -1706,7 +1715,7 @@ describe('generateOtelcolConfig', () => { is_preconfigured: false, }; - const result = generateOtelcolConfig(inputs, defaultOutput, undefined, proxy); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput, proxy }); expect(result.extensions?.['beatsauth/default']).toEqual({ proxy_url: 'http://proxy.example.com:3128', @@ -1729,7 +1738,7 @@ describe('generateOtelcolConfig', () => { is_preconfigured: false, }; - const result = generateOtelcolConfig(inputs, outputWithSSL, undefined, proxy); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithSSL, proxy }); expect(result.extensions?.['beatsauth/default']).toEqual({ ssl: { @@ -1742,7 +1751,7 @@ describe('generateOtelcolConfig', () => { }); it('should omit beatsauth from extensions and exporter auth when output has no ssl or proxy fields', () => { - const result = generateOtelcolConfig(inputs, defaultOutput); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput }); expect(result.extensions?.['beatsauth/default']).toBeUndefined(); expect(result.exporters?.['elasticsearch/default']).not.toHaveProperty('auth'); @@ -1760,7 +1769,7 @@ describe('generateOtelcolConfig', () => { }, }; - const result = generateOtelcolConfig(inputs, outputWithSecretKey); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithSecretKey }); expect(result.extensions?.['beatsauth/default']).toEqual({ secrets: { ssl: { key: { id: 'secret-id-abc123' } } }, @@ -1775,7 +1784,7 @@ describe('generateOtelcolConfig', () => { }, }; - const result = generateOtelcolConfig(inputs, outputWithSecretOnly); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithSecretOnly }); expect(result.extensions?.['beatsauth/default']).toEqual({ secrets: { ssl: { key: { id: 'my-secret-id' } } }, @@ -1792,7 +1801,7 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: 'flush_interval: 10s', }; - const result = generateOtelcolConfig(inputs, outputWithExporterYaml); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithExporterYaml }); expect(result.exporters?.['elasticsearch/default']).toEqual( expect.objectContaining({ flush_interval: '10s' }) @@ -1805,7 +1814,7 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: 'endpoints:\n - http://evil.com', }; - const result = generateOtelcolConfig(inputs, outputWithOverrides); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithOverrides }); expect(result.exporters?.['elasticsearch/default']).toEqual( expect.objectContaining({ @@ -1820,7 +1829,7 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: null, }; - const result = generateOtelcolConfig(inputs, outputWithNull); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithNull }); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, @@ -1828,7 +1837,7 @@ describe('generateOtelcolConfig', () => { }); it('should handle undefined otel_exporter_config_yaml gracefully', () => { - const result = generateOtelcolConfig(inputs, defaultOutput); + const result = generateOtelcolConfig({ inputs, dataOutput: defaultOutput }); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, @@ -1841,9 +1850,9 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: ': invalid yaml', }; - expect(() => generateOtelcolConfig(inputs, outputWithBadYaml)).not.toThrow(); + expect(() => generateOtelcolConfig({ inputs, dataOutput: outputWithBadYaml })).not.toThrow(); - const result = generateOtelcolConfig(inputs, outputWithBadYaml); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithBadYaml }); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, }); @@ -1855,7 +1864,7 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: 'just a string', }; - const result = generateOtelcolConfig(inputs, outputWithScalarYaml); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithScalarYaml }); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, @@ -1868,7 +1877,7 @@ describe('generateOtelcolConfig', () => { otel_exporter_config_yaml: '- a\n- b', }; - const result = generateOtelcolConfig(inputs, outputWithArrayYaml); + const result = generateOtelcolConfig({ inputs, dataOutput: outputWithArrayYaml }); expect(result.exporters?.['elasticsearch/default']).toEqual({ endpoints: defaultOutput.hosts, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts index ae154720c479b..cf1ec581a02f5 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/otel_collector.ts @@ -30,14 +30,21 @@ import { pkgToPkgKey } from '../epm/registry'; import { packagePolicyInputAllowsUndefinedDataStreamType } from '../../../common/services'; // Generate OTel Collector policy -export function generateOtelcolConfig( - inputs: FullAgentPolicyInput[] | TemplateAgentPolicyInput[], - dataOutput?: Output, - packageInfoCache?: Map, - proxy?: FleetProxy, - logger?: Logger, - defaultPackageInfo?: PackageInfo -): OTelCollectorConfig { +export function generateOtelcolConfig({ + inputs, + dataOutput, + packageInfoCache, + proxy, + logger, + defaultPackageInfo, +}: { + inputs: FullAgentPolicyInput[] | TemplateAgentPolicyInput[]; + dataOutput?: Output; + packageInfoCache?: Map; + proxy?: FleetProxy; + logger?: Logger; + defaultPackageInfo?: PackageInfo; +}): OTelCollectorConfig { const otelConfigs: OTelCollectorConfig[] = inputs .filter((input) => input.type === OTEL_COLLECTOR_INPUT_TYPE) .flatMap((input) => { diff --git a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/get_template_inputs.ts b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/get_template_inputs.ts index d3a7d8f5e7544..8854d544f8dd6 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/get_template_inputs.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/epm/packages/get_template_inputs.ts @@ -171,9 +171,9 @@ export async function getTemplateInputs( } } } - + const logger = appContextService.getLogger(); const assetsMap = await getAgentTemplateAssetsMap({ - logger: appContextService.getLogger(), + logger, packageInfo, savedObjectsClient: soClient, ignoreUnverified, @@ -222,7 +222,11 @@ export async function getTemplateInputs( let otelcolConfig; if (experimentalFeature.enableOtelIntegrations) { - otelcolConfig = generateOtelcolConfig(inputs, undefined, undefined, packageInfo); + otelcolConfig = generateOtelcolConfig({ + inputs, + logger, + defaultPackageInfo: packageInfo, + }); } // filter out the otelcol inputs, they will be added at the root of the config const filteredInputs = inputs.filter((input) => input.type !== OTEL_COLLECTOR_INPUT_TYPE); From d413decdc9952c49f896146421dbc56119d67bd3 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 31 Mar 2026 10:50:59 +0200 Subject: [PATCH 20/32] Remove unused schema --- .../fleet/server/types/models/output.ts | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index fc8c40238bdc2..bbe96e37d8d73 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -366,15 +366,6 @@ const BaseSOSchemaV9 = { secrets: schema.maybe(schema.object({}, { unknowns: 'allow' })), }; -// Schema representing the ingest-outputs ES SO shape at model version 9 -export const ElasticSearchSchemaV9 = schema.object({ - ...BaseSOSchemaV9, - type: schema.literal(outputType.Elasticsearch), - hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), - preset: schema.maybe(schema.string()), - write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), -}); - // Loose schema used for SO forwardCompatibility at model version 9. // Must be a plain schema.object() (not oneOf) so that .extends({}, { unknowns: 'ignore' }) works. // It accepts all fields that any output type may have, making all type-specific fields optional. @@ -406,55 +397,6 @@ export const OutputSOForwardCompatSchemaV9 = schema.object({ compression_level: schema.maybe(schema.number()), }); -// Schema representing the ingest-outputs SO shape at model version 9 (adds otel_exporter_config_yaml) -export const OutputSchemaV9 = schema.oneOf([ - ElasticSearchSchemaV9, - // Remote Elasticsearch - schema.object({ - ...BaseSOSchemaV9, - type: schema.literal(outputType.RemoteElasticsearch), - hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), - preset: schema.maybe(schema.string()), - service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), - }), - // Logstash - schema.object({ - ...BaseSOSchemaV9, - type: schema.literal(outputType.Logstash), - hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), - }), - // Kafka - schema.object({ - ...BaseSOSchemaV9, - type: schema.literal(outputType.Kafka), - hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), - version: schema.maybe(schema.string()), - compression: schema.maybe(schema.string()), - compression_level: schema.maybe(schema.number()), - timeout: schema.maybe(schema.number()), - broker_timeout: schema.maybe(schema.number()), - required_acks: schema.maybe(schema.number()), - client_id: schema.maybe(schema.string()), - username: schema.maybe(schema.string()), - password: schema.maybe(schema.string()), - partition: schema.maybe(schema.string()), - topic: schema.maybe(schema.string()), - topics: schema.maybe( - schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }) - ), - headers: schema.maybe( - schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }) - ), - hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), - round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), - random: schema.maybe(schema.object({}, { unknowns: 'allow' })), - sasl: schema.maybe(schema.object({}, { unknowns: 'allow' })), - auth_type: schema.maybe(schema.string()), - connection_type: schema.maybe(schema.string()), - }), -]); - export const OutputSchema = schema.oneOf([ schema.object({ ...ElasticSearchSchema }, { meta: { id: 'output_elasticsearch' } }), schema.object({ ...RemoteElasticSearchSchema }, { meta: { id: 'output_remote_elasticsearch' } }), From aab7dca9b04f449184b6f3303b3261145ca5ca63 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 31 Mar 2026 09:19:36 +0000 Subject: [PATCH 21/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 14280dea4044b..b49f199e74189 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "8f69da62e7da3561aac199a27ad0bf95f7e095903de613b7696f0d703afbf384", + "ingest-outputs": "f5be23144928894ae86b95f7ef6f396d451de92c44b8015978df61eed2926500", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: 77b915fc7efa36bae77dec172626543a9b13a3068e6e4690ee1f0788dbd3ed19", + "ingest-outputs|10.9.0: d127737801f9cd86356858671a73bdacaec6d188d4ac230fc41ad7f23a73b9ed", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From 992835d5614358e2550d6b3c7267c7a0300b5719 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 1 Apr 2026 11:05:48 +0200 Subject: [PATCH 22/32] Update schema to fix test --- .../fleet/server/types/models/output.ts | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index bbe96e37d8d73..c259ef95d5112 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -376,25 +376,44 @@ export const OutputSOForwardCompatSchemaV9 = schema.object({ preset: schema.maybe(schema.string()), write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - version: schema.maybe(schema.string()), - compression: schema.maybe(schema.string()), - timeout: schema.maybe(schema.number()), - broker_timeout: schema.maybe(schema.number()), - required_acks: schema.maybe(schema.number()), - client_id: schema.maybe(schema.string()), - username: schema.maybe(schema.string()), - password: schema.maybe(schema.string()), - partition: schema.maybe(schema.string()), - topic: schema.maybe(schema.string()), - topics: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 })), - headers: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 })), - hash: schema.maybe(schema.object({}, { unknowns: 'allow' })), - round_robin: schema.maybe(schema.object({}, { unknowns: 'allow' })), - random: schema.maybe(schema.object({}, { unknowns: 'allow' })), - sasl: schema.maybe(schema.object({}, { unknowns: 'allow' })), - auth_type: schema.maybe(schema.string()), - connection_type: schema.maybe(schema.string()), - compression_level: schema.maybe(schema.number()), + version: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + compression: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + timeout: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), + broker_timeout: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), + required_acks: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), + client_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + username: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + password: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + partition: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + topic: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + topics: schema.maybe( + schema.oneOf([ + schema.literal(null), + schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }), + ]) + ), + headers: schema.maybe( + schema.oneOf([ + schema.literal(null), + schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }), + ]) + ), + hash: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), + round_robin: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), + random: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), + sasl: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), + auth_type: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + connection_type: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + compression_level: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), }); export const OutputSchema = schema.oneOf([ From 59c611c5a745b3b48aa92c986df2de9f23f95658 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:26:20 +0000 Subject: [PATCH 23/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index b49f199e74189..0742488859667 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "f5be23144928894ae86b95f7ef6f396d451de92c44b8015978df61eed2926500", + "ingest-outputs": "ae216e9856758dc850b67571bab74a53200d452a918d054f35cd879132360c62", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: d127737801f9cd86356858671a73bdacaec6d188d4ac230fc41ad7f23a73b9ed", + "ingest-outputs|10.9.0: 9aa1d77a4e9b044f09ff60926b37f883a77ff2ff4174de3f8aa8c7831f74874b", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From 01d6cabf8d3fb6003c74a37d66523bf7489f86c9 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 1 Apr 2026 14:55:03 +0200 Subject: [PATCH 24/32] Fix schema again --- .../plugins/shared/fleet/server/types/models/output.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index c259ef95d5112..922d7f25db6dd 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -361,9 +361,13 @@ const BaseSOSchemaV9 = { // ssl is stored as a JSON-serialised string in the SO (binary ES mapping) ssl: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), proxy_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - shipper: schema.maybe(schema.object({}, { unknowns: 'allow' })), + shipper: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), allow_edit: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 1000 })), - secrets: schema.maybe(schema.object({}, { unknowns: 'allow' })), + secrets: schema.maybe( + schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) + ), }; // Loose schema used for SO forwardCompatibility at model version 9. From 5a49dbf595da25c0512e59c242cb7bc3de7dc80a Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:19:20 +0000 Subject: [PATCH 25/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 0742488859667..cf6b94e8a4da1 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "ae216e9856758dc850b67571bab74a53200d452a918d054f35cd879132360c62", + "ingest-outputs": "880173025d662ec0780c8a9eae3c7f310d14b2025434fb6a82000b5cee77b544", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: 9aa1d77a4e9b044f09ff60926b37f883a77ff2ff4174de3f8aa8c7831f74874b", + "ingest-outputs|10.9.0: e44a0f6c01d688401b4776d0668c665f464d114792084eb4d96634198ae44810", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From 6e666f5f3978ef3fda1447dbeb7b24f945c0f66a Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Thu, 2 Apr 2026 15:24:10 +0200 Subject: [PATCH 26/32] Simplify outputs schema --- .../__fixtures__/ingest-outputs/10.9.0.json | 3 +- .../fleet/server/saved_objects/index.ts | 5 +- .../fleet/server/types/models/output.ts | 84 ------------------- 3 files changed, 3 insertions(+), 89 deletions(-) diff --git a/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json index 0acd15b338f4a..7e0d38967ea81 100644 --- a/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json +++ b/packages/kbn-check-saved-objects-cli/src/migrations/__fixtures__/ingest-outputs/10.9.0.json @@ -16,8 +16,7 @@ "is_default": true, "is_default_monitoring": true, "hosts": ["https://localhost:9200"], - "preset": "balanced", - "otel_exporter_config_yaml": null + "preset": "balanced" } ] } diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index 34e5243ddda02..79e380c69c98f 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -45,7 +45,6 @@ import { SettingsSchemaV7, SettingsSchemaV8, PackagePolicySchemaV22, - OutputSOForwardCompatSchemaV9, } from '../types'; import { migrateSyntheticsPackagePolicyToV8120 } from './migrations/synthetics/to_v8_12_0'; @@ -814,8 +813,8 @@ export const getSavedObjectTypes = ( }, ], schemas: { - forwardCompatibility: OutputSOForwardCompatSchemaV9.extends({}, { unknowns: 'ignore' }), - create: OutputSOForwardCompatSchemaV9.extends({}, { unknowns: 'ignore' }), + forwardCompatibility: schema.object({}, { unknowns: 'ignore' }), + create: schema.object({}, { unknowns: 'allow' }), }, }, }, diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 922d7f25db6dd..3781a0fb6a156 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -336,90 +336,6 @@ const KafkaUpdateSchema = { ), }; -/** - * Versioned schemas for SO model versions (used by the check_saved_objects CLI) - * - * Note: these schemas reflect the SO storage shape, not the API shape. - * In particular, `ssl` is stored as a JSON-serialised string (binary mapping), - * `secrets` uses `{ id: string }` references, and all fields that are absent in - * a given output type must be marked optional. - */ - -// Base SO schema fields shared across all output types at model version 9. -// `ssl` is stored as a JSON string (binary), so we accept a plain string here. -const BaseSOSchemaV9 = { - id: schema.maybe(schema.string()), - name: schema.string(), - is_default: schema.boolean({ defaultValue: false }), - is_default_monitoring: schema.boolean({ defaultValue: false }), - is_internal: schema.maybe(schema.boolean()), - is_preconfigured: schema.maybe(schema.boolean()), - ca_sha256: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - ca_trusted_fingerprint: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - otel_exporter_config_yaml: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - // ssl is stored as a JSON-serialised string in the SO (binary ES mapping) - ssl: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - proxy_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - shipper: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), - allow_edit: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 1000 })), - secrets: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), -}; - -// Loose schema used for SO forwardCompatibility at model version 9. -// Must be a plain schema.object() (not oneOf) so that .extends({}, { unknowns: 'ignore' }) works. -// It accepts all fields that any output type may have, making all type-specific fields optional. -export const OutputSOForwardCompatSchemaV9 = schema.object({ - ...BaseSOSchemaV9, - type: schema.maybe(schema.string()), - hosts: schema.maybe(schema.arrayOf(schema.string(), { maxSize: 10 })), - preset: schema.maybe(schema.string()), - write_to_logs_streams: schema.maybe(schema.oneOf([schema.literal(null), schema.boolean()])), - service_token: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - version: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - compression: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - timeout: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), - broker_timeout: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), - required_acks: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), - client_id: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - username: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - password: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - partition: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - topic: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - topics: schema.maybe( - schema.oneOf([ - schema.literal(null), - schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }), - ]) - ), - headers: schema.maybe( - schema.oneOf([ - schema.literal(null), - schema.arrayOf(schema.object({}, { unknowns: 'allow' }), { maxSize: 100 }), - ]) - ), - hash: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), - round_robin: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), - random: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), - sasl: schema.maybe( - schema.oneOf([schema.literal(null), schema.object({}, { unknowns: 'allow' })]) - ), - auth_type: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - connection_type: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), - compression_level: schema.maybe(schema.oneOf([schema.literal(null), schema.number()])), -}); - export const OutputSchema = schema.oneOf([ schema.object({ ...ElasticSearchSchema }, { meta: { id: 'output_elasticsearch' } }), schema.object({ ...RemoteElasticSearchSchema }, { meta: { id: 'output_remote_elasticsearch' } }), From 5ad8e1eaa9243e1970198a0e43f238083c3a0d7c Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:54:12 +0000 Subject: [PATCH 27/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 8017224efdcfe..43510bb87c8ff 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "3618199f1f5cc7d4950005b107f6703d2615ce217e13d1d948f8f323c6bc8995", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "880173025d662ec0780c8a9eae3c7f310d14b2025434fb6a82000b5cee77b544", + "ingest-outputs": "3eaeecf03ff0db98b7038a6c04079d76c526bcc274ccc3efeb6037b895412e83", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "intercept_interaction_record": "02437dc1b92c7bc77563f8e8d758a13435080d493d21a5fd49d124d468cdeb20", @@ -848,7 +848,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: e44a0f6c01d688401b4776d0668c665f464d114792084eb4d96634198ae44810", + "ingest-outputs|10.9.0: d8c594690d009467e32f8690294a9548222abfe7c31885e4e10242b959b8afd0", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From b275adb1e008d1928ac1aad7c4c6b2280a43815c Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 7 Apr 2026 11:27:24 +0200 Subject: [PATCH 28/32] Address Macroscope app review --- .../plugins/shared/fleet/server/saved_objects/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index ab85f0baa9aa4..1b25eddde7a4e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -845,7 +845,13 @@ export const getSavedObjectTypes = ( }, ], schemas: { - forwardCompatibility: schema.object({}, { unknowns: 'ignore' }), + forwardCompatibility: (unknownAttributes: unknown) => { + const { otel_exporter_config_yaml: _, ...rest } = unknownAttributes as Record< + string, + unknown + >; + return rest; + }, create: schema.object({}, { unknowns: 'allow' }), }, }, From 8279ec6a69518a35f3bdc6ace17175ef3ce21713 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:54:03 +0000 Subject: [PATCH 29/32] Changes from node scripts/jest_integration -u src/core/server/integration_tests/ci_checks --- .../ci_checks/saved_objects/check_registered_types.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 5620edb8c7e9b..1dcb59486a566 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "498c2ba7abd4329a0d8b40efd98b4b16991107512d38141707f9f2e10521b367", "ingest-agent-policies": "ce61ba4808deecd27dce770799be59f212ebe1c0d76bcc4298aafa4cfce7aa15", "ingest-download-sources": "c87e062ef293585e85fccec0c865d7cef48e0ff9a919d7781d5f7627d275484b", - "ingest-outputs": "3eaeecf03ff0db98b7038a6c04079d76c526bcc274ccc3efeb6037b895412e83", + "ingest-outputs": "b5c8354ebfb71f5d50322c19787bbee2c5bb8fff5bfff46dc5683d19e94797a3", "ingest-package-policies": "7817460ef093393f7fce067f91186003e41246a69f32344b1cd68fe19f43a24e", "ingest_manager_settings": "6cd91fe6c52c516676d99021f51a4b3a162686880198ba3c556983f5fffbb5a3", "integration-config": "b5fba732f6835532e971c81f5703c3c2018fa93686e83a5c2dd9c56cb4e10578", @@ -853,7 +853,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-outputs|global: 3e72116f17fda6ec9c5269cb42eb42e3f686b313", "ingest-outputs|mappings: bdde67dc29cfd86a1195ed34a4417073728691c4", "ingest-outputs|schemas: da39a3ee5e6b4b0d3255bfef95601890afd80709", - "ingest-outputs|10.9.0: d8c594690d009467e32f8690294a9548222abfe7c31885e4e10242b959b8afd0", + "ingest-outputs|10.9.0: 50a325af05e7b9b3f7383f1e525388c9cecf00a14b9924409a262f6c3058840b", "ingest-outputs|10.8.0: 7fce3be244f92bb99b17c8757c39a49aec078ff90cae70971e42e202a574348c", "ingest-outputs|10.7.0: 2f5dfca42d489deaa60da45c020e92fdae21e5e3d0e5e60153c0d18039715bd7", "ingest-outputs|10.6.0: f224697a876b316945536829900df70c8c6af40c28ccd1b1be4854a1df7442dc", From 5d62d10a88b5206f518258f10a3543d4d4979218 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 7 Apr 2026 16:41:59 +0200 Subject: [PATCH 30/32] Fix oas docs --- .../shared/fleet/server/types/models/output.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 3781a0fb6a156..183c05d1859d8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -363,8 +363,11 @@ export const OutputResponseSchema = schema.object({ }); export const UpdateOutputSchema = schema.oneOf([ - schema.object({ ...ElasticSearchUpdateSchema }), - schema.object({ ...RemoteElasticSearchUpdateSchema }), - schema.object({ ...LogstashUpdateSchema }), - schema.object({ ...KafkaUpdateSchema }), + schema.object({ ...ElasticSearchUpdateSchema }, { meta: { id: 'update_output_elasticsearch' } }), + schema.object( + { ...RemoteElasticSearchUpdateSchema }, + { meta: { id: 'update_output_remote_elasticsearch' } } + ), + schema.object({ ...LogstashUpdateSchema }, { meta: { id: 'update_output_logstash' } }), + schema.object({ ...KafkaUpdateSchema }, { meta: { id: 'update_output_kafka' } }), ]); From 0fa70a8b1ef8165f82106626c40995c0fc3fbe45 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:14:10 +0000 Subject: [PATCH 31/32] Changes from make api-docs --- oas_docs/output/kibana.serverless.yaml | 2618 ++++++++++++------------ oas_docs/output/kibana.yaml | 1898 ++++++++--------- 2 files changed, 2270 insertions(+), 2246 deletions(-) diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 4f8afe2b425d0..3d8ebfb77264a 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -45327,965 +45327,226 @@ paths: application/json: schema: anyOf: - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - format: uri - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - preset: - enum: - - balanced - - custom - - throughput - - scale - - latency - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - shipper: + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_remote_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_logstash' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_kafka' + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + item: + anyOf: + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_remote_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_logstash' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_kafka' + required: + - item + description: 'OK: A successful request.' + '400': + content: + application/json: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + attributes: {} + error: + type: string + errorType: + type: string + message: + type: string + statusCode: + type: number + required: + - message + - attributes + description: A bad request. + summary: Update output + tags: + - Fleet outputs + x-metaTags: + - content: Kibana, Elastic Cloud Serverless + name: product_name + /api/fleet/outputs/{outputId}/health: + get: + description: |- + **Spaces method and path for this operation:** + +
get /s/{space_id}/api/fleet/outputs/{outputId}/health
+ + Refer to [Spaces](https://www.elastic.co/docs/deploy-manage/manage-spaces) for more information. + + [Required authorization] Route required privileges: fleet-settings-read. + operationId: get-fleet-outputs-outputid-health + parameters: + - in: path + name: outputId + required: true + schema: + type: string + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + message: + description: long message if unhealthy + type: string + state: + description: state of output, HEALTHY or DEGRADED + type: string + timestamp: + description: timestamp of reported state + type: string + required: + - state + - message + - timestamp + description: 'OK: A successful request.' + '400': + content: + application/json: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + attributes: {} + error: + type: string + errorType: + type: string + message: + type: string + statusCode: + type: number + required: + - message + - attributes + description: A bad request. + summary: Get the latest output health + tags: + - Fleet outputs + x-metaTags: + - content: Kibana, Elastic Cloud Serverless + name: product_name + /api/fleet/package_policies: + get: + operationId: get-fleet-package-policies + parameters: + - in: query + name: page + required: false + schema: + type: number + - in: query + name: perPage + required: false + schema: + type: number + - in: query + name: sortField + required: false + schema: + type: string + - in: query + name: sortOrder + required: false + schema: + enum: + - desc + - asc + type: string + - in: query + name: showUpgradeable + required: false + schema: + type: boolean + - in: query + name: kuery + required: false + schema: + type: string + - in: query + name: format + required: false + schema: + enum: + - simplified + - legacy + type: string + - in: query + name: withAgentCount + required: false + schema: + type: boolean + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + items: + items: additionalProperties: false - nullable: true type: object properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: + additional_datastreams_permissions: + description: Additional datastream permissions, that will be added to the agent policy. + items: + type: string + maxItems: 1000 nullable: true + type: array + agents: type: number - disk_queue_path: + cloud_connector_id: + description: ID of the cloud connector associated with this package policy. nullable: true type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: - type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - type: - enum: - - elasticsearch - type: string - write_to_logs_streams: - nullable: true - type: boolean - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - format: uri - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - kibana_api_key: - nullable: true - type: string - kibana_url: - nullable: true - type: string - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - preset: - enum: - - balanced - - custom - - throughput - - scale - - latency - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - service_token: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - service_token: - nullable: true - type: string - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: + cloud_connector_name: + description: Transient field for cloud connector name during creation. + maxLength: 255 + minLength: 1 nullable: true type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: + created_at: type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: + created_by: type: string - verification_mode: - enum: - - full - - none - - certificate - - strict + description: + description: Package policy description type: string - sync_integrations: - type: boolean - sync_uninstalled_integrations: - type: boolean - type: - enum: - - remote_elasticsearch - type: string - write_to_logs_streams: - nullable: true - type: boolean - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - ssl: - additionalProperties: false + elasticsearch: + additionalProperties: true type: object properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: - nullable: true - type: string - loadbalance: - nullable: true + privileges: + additionalProperties: true + type: object + properties: + cluster: + items: + type: string + maxItems: 100 + type: array + enabled: type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: - type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - type: - enum: - - logstash - type: string - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - auth_type: - enum: - - none - - user_pass - - ssl - - kerberos - type: string - broker_timeout: - type: number - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - client_id: - type: string - compression: - enum: - - gzip - - snappy - - lz4 - - none - type: string - compression_level: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: number - - not: {} - config_yaml: - nullable: true - type: string - connection_type: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - enum: - - plaintext - - encryption - type: string - - not: {} - hash: - additionalProperties: false - type: object - properties: - hash: - type: string - random: - type: boolean - headers: - items: - additionalProperties: false - type: object - properties: - key: - type: string - value: - type: string - required: - - key - - value - maxItems: 100 - type: array - hosts: - items: - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - default: false - type: boolean - is_default_monitoring: - default: false - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - key: - type: string - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - partition: - enum: - - random - - round_robin - - hash - type: string - password: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - not: {} - - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: string - - not: {} - proxy_id: - nullable: true - type: string - random: - additionalProperties: false - type: object - properties: - group_events: - type: number - required_acks: - enum: - - 1 - - 0 - - -1 - type: integer - round_robin: - additionalProperties: false - type: object - properties: - group_events: - type: number - sasl: - additionalProperties: false - nullable: true - type: object - properties: - mechanism: - enum: - - PLAIN - - SCRAM-SHA-256 - - SCRAM-SHA-512 - type: string - secrets: - additionalProperties: false - type: object - properties: - password: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - required: - - key - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: - nullable: true - type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: - type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - timeout: - type: number - topic: - type: string - type: - enum: - - kafka - type: string - username: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: string - - not: {} - version: - type: string - required: - - name - - compression_level - - connection_type - - username - - password - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - item: - anyOf: - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_elasticsearch' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_remote_elasticsearch' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_logstash' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_kafka' - required: - - item - description: 'OK: A successful request.' - '400': - content: - application/json: - schema: - additionalProperties: false - description: Generic Error - type: object - properties: - attributes: {} - error: - type: string - errorType: - type: string - message: - type: string - statusCode: - type: number - required: - - message - - attributes - description: A bad request. - summary: Update output - tags: - - Fleet outputs - x-metaTags: - - content: Kibana, Elastic Cloud Serverless - name: product_name - /api/fleet/outputs/{outputId}/health: - get: - description: |- - **Spaces method and path for this operation:** - -
get /s/{space_id}/api/fleet/outputs/{outputId}/health
- - Refer to [Spaces](https://www.elastic.co/docs/deploy-manage/manage-spaces) for more information. - - [Required authorization] Route required privileges: fleet-settings-read. - operationId: get-fleet-outputs-outputid-health - parameters: - - in: path - name: outputId - required: true - schema: - type: string - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - message: - description: long message if unhealthy - type: string - state: - description: state of output, HEALTHY or DEGRADED - type: string - timestamp: - description: timestamp of reported state - type: string - required: - - state - - message - - timestamp - description: 'OK: A successful request.' - '400': - content: - application/json: - schema: - additionalProperties: false - description: Generic Error - type: object - properties: - attributes: {} - error: - type: string - errorType: - type: string - message: - type: string - statusCode: - type: number - required: - - message - - attributes - description: A bad request. - summary: Get the latest output health - tags: - - Fleet outputs - x-metaTags: - - content: Kibana, Elastic Cloud Serverless - name: product_name - /api/fleet/package_policies: - get: - operationId: get-fleet-package-policies - parameters: - - in: query - name: page - required: false - schema: - type: number - - in: query - name: perPage - required: false - schema: - type: number - - in: query - name: sortField - required: false - schema: - type: string - - in: query - name: sortOrder - required: false - schema: - enum: - - desc - - asc - type: string - - in: query - name: showUpgradeable - required: false - schema: - type: boolean - - in: query - name: kuery - required: false - schema: - type: string - - in: query - name: format - required: false - schema: - enum: - - simplified - - legacy - type: string - - in: query - name: withAgentCount - required: false - schema: - type: boolean - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - items: - items: - additionalProperties: false - type: object - properties: - additional_datastreams_permissions: - description: Additional datastream permissions, that will be added to the agent policy. - items: - type: string - maxItems: 1000 - nullable: true - type: array - agents: - type: number - cloud_connector_id: - description: ID of the cloud connector associated with this package policy. - nullable: true - type: string - cloud_connector_name: - description: Transient field for cloud connector name during creation. - maxLength: 255 - minLength: 1 - nullable: true - type: string - created_at: - type: string - created_by: - type: string - description: - description: Package policy description - type: string - elasticsearch: - additionalProperties: true - type: object - properties: - privileges: - additionalProperties: true - type: object - properties: - cluster: - items: - type: string - maxItems: 100 - type: array - enabled: - type: boolean - id: - description: Package policy unique identifier. + id: + description: Package policy unique identifier. type: string inputs: anyOf: @@ -72707,11 +71968,193 @@ components: type: boolean ignore_missing: type: boolean - pattern: - minLength: 1 - type: string - replacement: - type: string + pattern: + minLength: 1 + type: string + replacement: + type: string + to: + minLength: 1 + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - from + - pattern + - replacement + - additionalProperties: false + description: Redact processor - Mask sensitive data using Grok patterns + type: object + properties: + action: + enum: + - redact + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + description: + description: Human-readable notes about this processor step + type: string + from: + description: Source field to redact sensitive data from + minLength: 1 + type: string + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + description: Skip processing when source field is missing (defaults to true) + type: boolean + pattern_definitions: + additionalProperties: + type: string + description: Custom pattern definitions to use in the patterns + type: object + patterns: + description: Grok patterns to match sensitive data (for example, "%{IP:client}", "%{EMAILADDRESS:email}") + items: + description: A non-empty string. + minLength: 1 + type: string + minItems: 1 + type: array + prefix: + description: Prefix to prepend to the redacted pattern name (defaults to "<") + type: string + suffix: + description: Suffix to append to the redacted pattern name (defaults to ">") + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - from + - patterns + - additionalProperties: false + type: object + properties: + action: + enum: + - uppercase + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + description: + description: Human-readable notes about this processor step + type: string + from: + minLength: 1 + type: string + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + type: boolean + to: + minLength: 1 + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - from + - additionalProperties: false + type: object + properties: + action: + enum: + - lowercase + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + description: + description: Human-readable notes about this processor step + type: string + from: + minLength: 1 + type: string + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + type: boolean + to: + minLength: 1 + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - from + - additionalProperties: false + type: object + properties: + action: + enum: + - trim + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + description: + description: Human-readable notes about this processor step + type: string + from: + minLength: 1 + type: string + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + type: boolean + to: + minLength: 1 + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - from + - additionalProperties: false + type: object + properties: + action: + enum: + - join + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + delimiter: + type: string + description: + description: Human-readable notes about this processor step + type: string + from: + items: + minLength: 1 + type: string + minItems: 1 + type: array + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + type: boolean to: minLength: 1 type: string @@ -72721,15 +72164,15 @@ components: required: - action - from - - pattern - - replacement + - delimiter + - to - additionalProperties: false - description: Redact processor - Mask sensitive data using Grok patterns + description: Split processor - Split a field value into an array using a separator type: object properties: action: enum: - - redact + - split type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72739,33 +72182,25 @@ components: description: Human-readable notes about this processor step type: string from: - description: Source field to redact sensitive data from + description: Source field to split into an array minLength: 1 type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean ignore_missing: - description: Skip processing when source field is missing (defaults to true) + description: Skip processing when source field is missing type: boolean - pattern_definitions: - additionalProperties: - type: string - description: Custom pattern definitions to use in the patterns - type: object - patterns: - description: Grok patterns to match sensitive data (for example, "%{IP:client}", "%{EMAILADDRESS:email}") - items: - description: A non-empty string. - minLength: 1 - type: string - minItems: 1 - type: array - prefix: - description: Prefix to prepend to the redacted pattern name (defaults to "<") + preserve_trailing: + description: Preserve empty trailing fields in the split result + type: boolean + separator: + description: Regex separator used to split the field value into an array + minLength: 1 type: string - suffix: - description: Suffix to append to the redacted pattern name (defaults to ">") + to: + description: Target field for the split array (defaults to source) + minLength: 1 type: string where: $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' @@ -72773,13 +72208,13 @@ components: required: - action - from - - patterns + - separator - additionalProperties: false type: object properties: action: enum: - - uppercase + - sort type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72789,14 +72224,23 @@ components: description: Human-readable notes about this processor step type: string from: + description: Array field to sort minLength: 1 type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean ignore_missing: + description: Skip processing when source field is missing type: boolean + order: + description: Sort order - "asc" (ascending) or "desc" (descending). Defaults to "asc" + enum: + - asc + - desc + type: string to: + description: Target field for the sorted array (defaults to source) minLength: 1 type: string where: @@ -72806,11 +72250,12 @@ components: - action - from - additionalProperties: false + description: Convert processor - Change the data type of a field value (integer, long, double, boolean, or string) type: object properties: action: enum: - - lowercase + - convert type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72820,28 +72265,41 @@ components: description: Human-readable notes about this processor step type: string from: + description: Source field to convert to a different data type minLength: 1 type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean ignore_missing: + description: Skip processing when source field is missing type: boolean to: + description: Target field for the converted value (defaults to source) minLength: 1 type: string + type: + description: 'Target data type: integer, long, double, boolean, or string' + enum: + - integer + - long + - double + - boolean + - string + type: string where: $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' description: Conditional expression controlling whether this processor runs required: - action - from + - type - additionalProperties: false type: object properties: action: enum: - - trim + - concat type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72851,8 +72309,33 @@ components: description: Human-readable notes about this processor step type: string from: - minLength: 1 - type: string + items: + anyOf: + - type: object + properties: + type: + enum: + - field + type: string + value: + minLength: 1 + type: string + required: + - type + - value + - type: object + properties: + type: + enum: + - literal + type: string + value: + type: string + required: + - type + - value + minItems: 1 + type: array ignore_failure: description: Continue pipeline execution if this processor fails type: boolean @@ -72867,51 +72350,127 @@ components: required: - action - from + - to + - allOf: + - additionalProperties: false + type: object + properties: + action: + enum: + - network_direction + type: string + customIdentifier: + description: Custom identifier to correlate this processor across outputs + minLength: 1 + type: string + description: + description: Human-readable notes about this processor step + type: string + destination_ip: + minLength: 1 + type: string + ignore_failure: + description: Continue pipeline execution if this processor fails + type: boolean + ignore_missing: + type: boolean + source_ip: + minLength: 1 + type: string + target_field: + minLength: 1 + type: string + where: + $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' + description: Conditional expression controlling whether this processor runs + required: + - action + - source_ip + - destination_ip + - anyOf: + - additionalProperties: false + type: object + properties: + internal_networks: + items: + type: string + type: array + required: + - internal_networks + - additionalProperties: false + type: object + properties: + internal_networks_field: + minLength: 1 + type: string + required: + - internal_networks_field - additionalProperties: false + description: JsonExtract processor - Extract values from JSON strings using JSONPath-like selectors type: object properties: action: enum: - - join + - json_extract type: string customIdentifier: description: Custom identifier to correlate this processor across outputs minLength: 1 type: string - delimiter: - type: string description: description: Human-readable notes about this processor step type: string - from: + extractions: + description: List of extraction specifications items: - minLength: 1 - type: string + description: A single extraction specification + type: object + properties: + selector: + description: JSONPath-like selector to extract value (e.g., "user.id", "$.metadata.client.ip", "items[0].name") + minLength: 1 + type: string + target_field: + description: Target field to store the extracted value + minLength: 1 + type: string + type: + description: Data type for the extracted value. Defaults to "keyword". Ensures consistent types across transpilers. + enum: + - keyword + - integer + - long + - double + - boolean + type: string + required: + - selector + - target_field minItems: 1 type: array + field: + description: Source field containing the JSON string to parse + minLength: 1 + type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean ignore_missing: + description: Skip processing when source field is missing type: boolean - to: - minLength: 1 - type: string where: $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' description: Conditional expression controlling whether this processor runs required: - action - - from - - delimiter - - to + - field + - extractions - additionalProperties: false - description: Split processor - Split a field value into an array using a separator type: object properties: action: enum: - - split + - enrich type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72920,25 +72479,18 @@ components: description: description: Human-readable notes about this processor step type: string - from: - description: Source field to split into an array - minLength: 1 - type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean ignore_missing: - description: Skip processing when source field is missing type: boolean - preserve_trailing: - description: Preserve empty trailing fields in the split result + override: type: boolean - separator: - description: Regex separator used to split the field value into an array + policy_name: + description: A non-empty string. minLength: 1 type: string to: - description: Target field for the split array (defaults to source) minLength: 1 type: string where: @@ -72946,14 +72498,16 @@ components: description: Conditional expression controlling whether this processor runs required: - action - - from - - separator + - policy_name + - to - additionalProperties: false + description: Manual ingest pipeline wrapper around native Elasticsearch processors type: object properties: action: + description: Manual ingest pipeline - executes raw Elasticsearch ingest processors enum: - - sort + - manual_ingest_pipeline type: string customIdentifier: description: Custom identifier to correlate this processor across outputs @@ -72962,329 +72516,787 @@ components: description: description: Human-readable notes about this processor step type: string - from: - description: Array field to sort - minLength: 1 - type: string ignore_failure: description: Continue pipeline execution if this processor fails type: boolean - ignore_missing: - description: Skip processing when source field is missing - type: boolean - order: - description: Sort order - "asc" (ascending) or "desc" (descending). Defaults to "asc" - enum: - - asc - - desc - type: string - to: - description: Target field for the sorted array (defaults to source) - minLength: 1 + on_failure: + description: Fallback processors to run when a processor fails + items: + additionalProperties: {} + type: object + type: array + processors: + description: List of raw Elasticsearch ingest processors to run + items: + additionalProperties: {} + type: object + type: array + tag: + description: Optional ingest processor tag for Elasticsearch type: string where: $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' description: Conditional expression controlling whether this processor runs required: - action - - from - - additionalProperties: false - description: Convert processor - Change the data type of a field value (integer, long, double, boolean, or string) + - processors + - $ref: '#/components/schemas/Kibana_HTTP_APIs_StreamlangConditionBlock' + Kibana_HTTP_APIs_StreamUpsertRequest: + anyOf: + - $ref: '#/components/schemas/Kibana_HTTP_APIs_WiredStreamUpsertRequest' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_ClassicStreamUpsertRequest' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_QueryStreamUpsertRequest' + Kibana_HTTP_APIs_update_output_elasticsearch: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + format: uri + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + preset: + enum: + - balanced + - custom + - throughput + - scale + - latency + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + type: + enum: + - elasticsearch + type: string + write_to_logs_streams: + nullable: true + type: boolean + title: update_output_elasticsearch + type: object + Kibana_HTTP_APIs_update_output_kafka: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + auth_type: + enum: + - none + - user_pass + - ssl + - kerberos + type: string + broker_timeout: + type: number + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + client_id: + type: string + compression: + enum: + - gzip + - snappy + - lz4 + - none + type: string + compression_level: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: number + - not: {} + config_yaml: + nullable: true + type: string + connection_type: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - enum: + - plaintext + - encryption + type: string + - not: {} + hash: + additionalProperties: false + type: object + properties: + hash: + type: string + random: + type: boolean + headers: + items: + additionalProperties: false + type: object + properties: + key: + type: string + value: + type: string + required: + - key + - value + maxItems: 100 + type: array + hosts: + items: + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + default: false + type: boolean + is_default_monitoring: + default: false + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + key: + type: string + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + partition: + enum: + - random + - round_robin + - hash + type: string + password: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - not: {} + - anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: string + - not: {} + proxy_id: + nullable: true + type: string + random: + additionalProperties: false + type: object + properties: + group_events: + type: number + required_acks: + enum: + - 1 + - 0 + - -1 + type: integer + round_robin: + additionalProperties: false + type: object + properties: + group_events: + type: number + sasl: + additionalProperties: false + nullable: true + type: object + properties: + mechanism: + enum: + - PLAIN + - SCRAM-SHA-256 + - SCRAM-SHA-512 + type: string + secrets: + additionalProperties: false + type: object + properties: + password: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + ssl: + additionalProperties: false type: object properties: - action: - enum: - - convert - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - from: - description: Source field to convert to a different data type - minLength: 1 - type: string - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - ignore_missing: - description: Skip processing when source field is missing - type: boolean - to: - description: Target field for the converted value (defaults to source) - minLength: 1 - type: string - type: - description: 'Target data type: integer, long, double, boolean, or string' - enum: - - integer - - long - - double - - boolean - - string - type: string - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string required: - - action - - from - - type - - additionalProperties: false + - key + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + timeout: + type: number + topic: + type: string + type: + enum: + - kafka + type: string + username: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: string + - not: {} + version: + type: string + required: + - name + - compression_level + - connection_type + - username + - password + title: update_output_kafka + type: object + Kibana_HTTP_APIs_update_output_logstash: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + ssl: + additionalProperties: false type: object properties: - action: - enum: - - concat - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - from: - items: - anyOf: - - type: object - properties: - type: - enum: - - field - type: string - value: - minLength: 1 - type: string - required: - - type - - value - - type: object - properties: - type: - enum: - - literal - type: string - value: - type: string - required: - - type - - value - minItems: 1 - type: array - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - ignore_missing: - type: boolean - to: - minLength: 1 - type: string - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs - required: - - action - - from - - to - - allOf: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + type: + enum: + - logstash + type: string + title: update_output_logstash + type: object + Kibana_HTTP_APIs_update_output_remote_elasticsearch: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + format: uri + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + preset: + enum: + - balanced + - custom + - throughput + - scale + - latency + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + service_token: + anyOf: - additionalProperties: false type: object properties: - action: - enum: - - network_direction - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - destination_ip: - minLength: 1 - type: string - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - ignore_missing: - type: boolean - source_ip: - minLength: 1 + hash: type: string - target_field: - minLength: 1 + id: type: string - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs required: - - action - - source_ip - - destination_ip - - anyOf: - - additionalProperties: false - type: object - properties: - internal_networks: - items: - type: string - type: array - required: - - internal_networks + - id + - type: string + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: - additionalProperties: false type: object properties: - internal_networks_field: - minLength: 1 + hash: + type: string + id: type: string required: - - internal_networks_field - - additionalProperties: false - description: JsonExtract processor - Extract values from JSON strings using JSONPath-like selectors - type: object - properties: - action: - enum: - - json_extract - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - extractions: - description: List of extraction specifications - items: - description: A single extraction specification - type: object - properties: - selector: - description: JSONPath-like selector to extract value (e.g., "user.id", "$.metadata.client.ip", "items[0].name") - minLength: 1 - type: string - target_field: - description: Target field to store the extracted value - minLength: 1 - type: string - type: - description: Data type for the extracted value. Defaults to "keyword". Ensures consistent types across transpilers. - enum: - - keyword - - integer - - long - - double - - boolean - type: string - required: - - selector - - target_field - minItems: 1 - type: array - field: - description: Source field containing the JSON string to parse - minLength: 1 - type: string - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - ignore_missing: - description: Skip processing when source field is missing - type: boolean - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs - required: - - action - - field - - extractions - - additionalProperties: false - type: object - properties: - action: - enum: - - enrich - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - ignore_missing: - type: boolean - override: - type: boolean - policy_name: - description: A non-empty string. - minLength: 1 - type: string - to: - minLength: 1 - type: string - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs - required: - - action - - policy_name - - to - - additionalProperties: false - description: Manual ingest pipeline wrapper around native Elasticsearch processors - type: object - properties: - action: - description: Manual ingest pipeline - executes raw Elasticsearch ingest processors - enum: - - manual_ingest_pipeline - type: string - customIdentifier: - description: Custom identifier to correlate this processor across outputs - minLength: 1 - type: string - description: - description: Human-readable notes about this processor step - type: string - ignore_failure: - description: Continue pipeline execution if this processor fails - type: boolean - on_failure: - description: Fallback processors to run when a processor fails - items: - additionalProperties: {} - type: object - type: array - processors: - description: List of raw Elasticsearch ingest processors to run - items: - additionalProperties: {} - type: object - type: array - tag: - description: Optional ingest processor tag for Elasticsearch - type: string - where: - $ref: '#/components/schemas/Kibana_HTTP_APIs_Condition' - description: Conditional expression controlling whether this processor runs - required: - - action - - processors - - $ref: '#/components/schemas/Kibana_HTTP_APIs_StreamlangConditionBlock' - Kibana_HTTP_APIs_StreamUpsertRequest: - anyOf: - - $ref: '#/components/schemas/Kibana_HTTP_APIs_WiredStreamUpsertRequest' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_ClassicStreamUpsertRequest' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_QueryStreamUpsertRequest' + - id + - type: string + service_token: + nullable: true + type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + sync_integrations: + type: boolean + sync_uninstalled_integrations: + type: boolean + type: + enum: + - remote_elasticsearch + type: string + write_to_logs_streams: + nullable: true + type: boolean + title: update_output_remote_elasticsearch + type: object Kibana_HTTP_APIs_WiredStreamUpsertRequest: additionalProperties: false type: object diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 5579a66f820c7..e06fbca9dd76c 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -47902,965 +47902,226 @@ paths: application/json: schema: anyOf: - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - format: uri - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - preset: - enum: - - balanced - - custom - - throughput - - scale - - latency - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: - nullable: true - type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_remote_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_logstash' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_update_output_kafka' + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + item: + anyOf: + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_remote_elasticsearch' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_logstash' + - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_kafka' + required: + - item + description: 'OK: A successful request.' + '400': + content: + application/json: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + attributes: {} + error: + type: string + errorType: + type: string + message: + type: string + statusCode: + type: number + required: + - message + - attributes + description: A bad request. + summary: Update output + tags: + - Fleet outputs + x-metaTags: + - content: Kibana + name: product_name + /api/fleet/outputs/{outputId}/health: + get: + description: |- + **Spaces method and path for this operation:** + +
get /s/{space_id}/api/fleet/outputs/{outputId}/health
+ + Refer to [Spaces](https://www.elastic.co/docs/deploy-manage/manage-spaces) for more information. + + [Required authorization] Route required privileges: fleet-settings-read. + operationId: get-fleet-outputs-outputid-health + parameters: + - in: path + name: outputId + required: true + schema: + type: string + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + message: + description: long message if unhealthy + type: string + state: + description: state of output, HEALTHY or DEGRADED + type: string + timestamp: + description: timestamp of reported state + type: string + required: + - state + - message + - timestamp + description: 'OK: A successful request.' + '400': + content: + application/json: + schema: + additionalProperties: false + description: Generic Error + type: object + properties: + attributes: {} + error: + type: string + errorType: + type: string + message: + type: string + statusCode: + type: number + required: + - message + - attributes + description: A bad request. + summary: Get the latest output health + tags: + - Fleet outputs + x-metaTags: + - content: Kibana + name: product_name + /api/fleet/package_policies: + get: + operationId: get-fleet-package-policies + parameters: + - in: query + name: page + required: false + schema: + type: number + - in: query + name: perPage + required: false + schema: + type: number + - in: query + name: sortField + required: false + schema: + type: string + - in: query + name: sortOrder + required: false + schema: + enum: + - desc + - asc + type: string + - in: query + name: showUpgradeable + required: false + schema: + type: boolean + - in: query + name: kuery + required: false + schema: + type: string + - in: query + name: format + required: false + schema: + enum: + - simplified + - legacy + type: string + - in: query + name: withAgentCount + required: false + schema: + type: boolean + responses: + '200': + content: + application/json: + schema: + additionalProperties: false + type: object + properties: + items: + items: additionalProperties: false - nullable: true type: object properties: - certificate: - type: string - certificate_authorities: + additional_datastreams_permissions: + description: Additional datastream permissions, that will be added to the agent policy. items: type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - type: - enum: - - elasticsearch - type: string - write_to_logs_streams: - nullable: true - type: boolean - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - format: uri - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - kibana_api_key: - nullable: true - type: string - kibana_url: - nullable: true - type: string - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - preset: - enum: - - balanced - - custom - - throughput - - scale - - latency - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - service_token: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - service_token: - nullable: true - type: string - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: + maxItems: 1000 nullable: true + type: array + agents: type: number - disk_queue_path: + cloud_connector_id: + description: ID of the cloud connector associated with this package policy. nullable: true type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: + cloud_connector_name: + description: Transient field for cloud connector name during creation. + maxLength: 255 + minLength: 1 nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: + created_at: type: string - verification_mode: - enum: - - full - - none - - certificate - - strict + created_by: type: string - sync_integrations: - type: boolean - sync_uninstalled_integrations: - type: boolean - type: - enum: - - remote_elasticsearch - type: string - write_to_logs_streams: - nullable: true - type: boolean - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - config_yaml: - nullable: true - type: string - hosts: - items: - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - type: boolean - is_default_monitoring: - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - proxy_id: - nullable: true - type: string - secrets: - additionalProperties: false - type: object - properties: - ssl: - additionalProperties: false + description: + description: Package policy description + type: string + elasticsearch: + additionalProperties: true type: object properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: - nullable: true - type: string - loadbalance: - nullable: true + privileges: + additionalProperties: true + type: object + properties: + cluster: + items: + type: string + maxItems: 100 + type: array + enabled: type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: - type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - type: - enum: - - logstash - type: string - - additionalProperties: false - type: object - properties: - allow_edit: - items: - type: string - maxItems: 1000 - type: array - auth_type: - enum: - - none - - user_pass - - ssl - - kerberos - type: string - broker_timeout: - type: number - ca_sha256: - nullable: true - type: string - ca_trusted_fingerprint: - nullable: true - type: string - client_id: - type: string - compression: - enum: - - gzip - - snappy - - lz4 - - none - type: string - compression_level: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: number - - not: {} - config_yaml: - nullable: true - type: string - connection_type: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - enum: - - plaintext - - encryption - type: string - - not: {} - hash: - additionalProperties: false - type: object - properties: - hash: - type: string - random: - type: boolean - headers: - items: - additionalProperties: false - type: object - properties: - key: - type: string - value: - type: string - required: - - key - - value - maxItems: 100 - type: array - hosts: - items: - type: string - maxItems: 10 - minItems: 1 - type: array - id: - type: string - is_default: - default: false - type: boolean - is_default_monitoring: - default: false - type: boolean - is_internal: - type: boolean - is_preconfigured: - type: boolean - key: - type: string - name: - type: string - otel_exporter_config_yaml: - nullable: true - type: string - partition: - enum: - - random - - round_robin - - hash - type: string - password: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - not: {} - - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: string - - not: {} - proxy_id: - nullable: true - type: string - random: - additionalProperties: false - type: object - properties: - group_events: - type: number - required_acks: - enum: - - 1 - - 0 - - -1 - type: integer - round_robin: - additionalProperties: false - type: object - properties: - group_events: - type: number - sasl: - additionalProperties: false - nullable: true - type: object - properties: - mechanism: - enum: - - PLAIN - - SCRAM-SHA-256 - - SCRAM-SHA-512 - type: string - secrets: - additionalProperties: false - type: object - properties: - password: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - ssl: - additionalProperties: false - type: object - properties: - key: - anyOf: - - additionalProperties: false - type: object - properties: - hash: - type: string - id: - type: string - required: - - id - - type: string - required: - - key - shipper: - additionalProperties: false - nullable: true - type: object - properties: - compression_level: - nullable: true - type: number - disk_queue_compression_enabled: - nullable: true - type: boolean - disk_queue_enabled: - default: false - nullable: true - type: boolean - disk_queue_encryption_enabled: - nullable: true - type: boolean - disk_queue_max_size: - nullable: true - type: number - disk_queue_path: - nullable: true - type: string - loadbalance: - nullable: true - type: boolean - max_batch_bytes: - nullable: true - type: number - mem_queue_events: - nullable: true - type: number - queue_flush_timeout: - nullable: true - type: number - required: - - disk_queue_path - - disk_queue_max_size - - disk_queue_encryption_enabled - - disk_queue_compression_enabled - - compression_level - - loadbalance - - mem_queue_events - - queue_flush_timeout - - max_batch_bytes - ssl: - additionalProperties: false - nullable: true - type: object - properties: - certificate: - type: string - certificate_authorities: - items: - type: string - maxItems: 10 - type: array - key: - type: string - verification_mode: - enum: - - full - - none - - certificate - - strict - type: string - timeout: - type: number - topic: - type: string - type: - enum: - - kafka - type: string - username: - anyOf: - - items: {} - type: array - - type: boolean - - type: number - - type: object - - type: string - nullable: true - oneOf: - - type: string - - not: {} - version: - type: string - required: - - name - - compression_level - - connection_type - - username - - password - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - item: - anyOf: - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_elasticsearch' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_remote_elasticsearch' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_logstash' - - $ref: '#/components/schemas/Kibana_HTTP_APIs_output_kafka' - required: - - item - description: 'OK: A successful request.' - '400': - content: - application/json: - schema: - additionalProperties: false - description: Generic Error - type: object - properties: - attributes: {} - error: - type: string - errorType: - type: string - message: - type: string - statusCode: - type: number - required: - - message - - attributes - description: A bad request. - summary: Update output - tags: - - Fleet outputs - x-metaTags: - - content: Kibana - name: product_name - /api/fleet/outputs/{outputId}/health: - get: - description: |- - **Spaces method and path for this operation:** - -
get /s/{space_id}/api/fleet/outputs/{outputId}/health
- - Refer to [Spaces](https://www.elastic.co/docs/deploy-manage/manage-spaces) for more information. - - [Required authorization] Route required privileges: fleet-settings-read. - operationId: get-fleet-outputs-outputid-health - parameters: - - in: path - name: outputId - required: true - schema: - type: string - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - message: - description: long message if unhealthy - type: string - state: - description: state of output, HEALTHY or DEGRADED - type: string - timestamp: - description: timestamp of reported state - type: string - required: - - state - - message - - timestamp - description: 'OK: A successful request.' - '400': - content: - application/json: - schema: - additionalProperties: false - description: Generic Error - type: object - properties: - attributes: {} - error: - type: string - errorType: - type: string - message: - type: string - statusCode: - type: number - required: - - message - - attributes - description: A bad request. - summary: Get the latest output health - tags: - - Fleet outputs - x-metaTags: - - content: Kibana - name: product_name - /api/fleet/package_policies: - get: - operationId: get-fleet-package-policies - parameters: - - in: query - name: page - required: false - schema: - type: number - - in: query - name: perPage - required: false - schema: - type: number - - in: query - name: sortField - required: false - schema: - type: string - - in: query - name: sortOrder - required: false - schema: - enum: - - desc - - asc - type: string - - in: query - name: showUpgradeable - required: false - schema: - type: boolean - - in: query - name: kuery - required: false - schema: - type: string - - in: query - name: format - required: false - schema: - enum: - - simplified - - legacy - type: string - - in: query - name: withAgentCount - required: false - schema: - type: boolean - responses: - '200': - content: - application/json: - schema: - additionalProperties: false - type: object - properties: - items: - items: - additionalProperties: false - type: object - properties: - additional_datastreams_permissions: - description: Additional datastream permissions, that will be added to the agent policy. - items: - type: string - maxItems: 1000 - nullable: true - type: array - agents: - type: number - cloud_connector_id: - description: ID of the cloud connector associated with this package policy. - nullable: true - type: string - cloud_connector_name: - description: Transient field for cloud connector name during creation. - maxLength: 255 - minLength: 1 - nullable: true - type: string - created_at: - type: string - created_by: - type: string - description: - description: Package policy description - type: string - elasticsearch: - additionalProperties: true - type: object - properties: - privileges: - additionalProperties: true - type: object - properties: - cluster: - items: - type: string - maxItems: 100 - type: array - enabled: - type: boolean - id: - description: Package policy unique identifier. + id: + description: Package policy unique identifier. type: string inputs: anyOf: @@ -84215,6 +83476,757 @@ components: - $ref: '#/components/schemas/Kibana_HTTP_APIs_WiredStreamUpsertRequest' - $ref: '#/components/schemas/Kibana_HTTP_APIs_ClassicStreamUpsertRequest' - $ref: '#/components/schemas/Kibana_HTTP_APIs_QueryStreamUpsertRequest' + Kibana_HTTP_APIs_update_output_elasticsearch: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + format: uri + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + preset: + enum: + - balanced + - custom + - throughput + - scale + - latency + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + type: + enum: + - elasticsearch + type: string + write_to_logs_streams: + nullable: true + type: boolean + title: update_output_elasticsearch + type: object + Kibana_HTTP_APIs_update_output_kafka: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + auth_type: + enum: + - none + - user_pass + - ssl + - kerberos + type: string + broker_timeout: + type: number + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + client_id: + type: string + compression: + enum: + - gzip + - snappy + - lz4 + - none + type: string + compression_level: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: number + - not: {} + config_yaml: + nullable: true + type: string + connection_type: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - enum: + - plaintext + - encryption + type: string + - not: {} + hash: + additionalProperties: false + type: object + properties: + hash: + type: string + random: + type: boolean + headers: + items: + additionalProperties: false + type: object + properties: + key: + type: string + value: + type: string + required: + - key + - value + maxItems: 100 + type: array + hosts: + items: + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + default: false + type: boolean + is_default_monitoring: + default: false + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + key: + type: string + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + partition: + enum: + - random + - round_robin + - hash + type: string + password: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - not: {} + - anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: string + - not: {} + proxy_id: + nullable: true + type: string + random: + additionalProperties: false + type: object + properties: + group_events: + type: number + required_acks: + enum: + - 1 + - 0 + - -1 + type: integer + round_robin: + additionalProperties: false + type: object + properties: + group_events: + type: number + sasl: + additionalProperties: false + nullable: true + type: object + properties: + mechanism: + enum: + - PLAIN + - SCRAM-SHA-256 + - SCRAM-SHA-512 + type: string + secrets: + additionalProperties: false + type: object + properties: + password: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + required: + - key + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + timeout: + type: number + topic: + type: string + type: + enum: + - kafka + type: string + username: + anyOf: + - items: {} + type: array + - type: boolean + - type: number + - type: object + - type: string + nullable: true + oneOf: + - type: string + - not: {} + version: + type: string + required: + - name + - compression_level + - connection_type + - username + - password + title: update_output_kafka + type: object + Kibana_HTTP_APIs_update_output_logstash: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + type: + enum: + - logstash + type: string + title: update_output_logstash + type: object + Kibana_HTTP_APIs_update_output_remote_elasticsearch: + additionalProperties: false + properties: + allow_edit: + items: + type: string + maxItems: 1000 + type: array + ca_sha256: + nullable: true + type: string + ca_trusted_fingerprint: + nullable: true + type: string + config_yaml: + nullable: true + type: string + hosts: + items: + format: uri + type: string + maxItems: 10 + minItems: 1 + type: array + id: + type: string + is_default: + type: boolean + is_default_monitoring: + type: boolean + is_internal: + type: boolean + is_preconfigured: + type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string + name: + type: string + otel_exporter_config_yaml: + nullable: true + type: string + preset: + enum: + - balanced + - custom + - throughput + - scale + - latency + type: string + proxy_id: + nullable: true + type: string + secrets: + additionalProperties: false + type: object + properties: + service_token: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + ssl: + additionalProperties: false + type: object + properties: + key: + anyOf: + - additionalProperties: false + type: object + properties: + hash: + type: string + id: + type: string + required: + - id + - type: string + service_token: + nullable: true + type: string + shipper: + additionalProperties: false + nullable: true + type: object + properties: + compression_level: + nullable: true + type: number + disk_queue_compression_enabled: + nullable: true + type: boolean + disk_queue_enabled: + default: false + nullable: true + type: boolean + disk_queue_encryption_enabled: + nullable: true + type: boolean + disk_queue_max_size: + nullable: true + type: number + disk_queue_path: + nullable: true + type: string + loadbalance: + nullable: true + type: boolean + max_batch_bytes: + nullable: true + type: number + mem_queue_events: + nullable: true + type: number + queue_flush_timeout: + nullable: true + type: number + required: + - disk_queue_path + - disk_queue_max_size + - disk_queue_encryption_enabled + - disk_queue_compression_enabled + - compression_level + - loadbalance + - mem_queue_events + - queue_flush_timeout + - max_batch_bytes + ssl: + additionalProperties: false + nullable: true + type: object + properties: + certificate: + type: string + certificate_authorities: + items: + type: string + maxItems: 10 + type: array + key: + type: string + verification_mode: + enum: + - full + - none + - certificate + - strict + type: string + sync_integrations: + type: boolean + sync_uninstalled_integrations: + type: boolean + type: + enum: + - remote_elasticsearch + type: string + write_to_logs_streams: + nullable: true + type: boolean + title: update_output_remote_elasticsearch + type: object Kibana_HTTP_APIs_WiredStreamUpsertRequest: additionalProperties: false type: object From ceacab8481cd291c77cec71c00683eb9c86f6a34 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Wed, 8 Apr 2026 11:21:31 +0200 Subject: [PATCH 32/32] Add allowlist for API contract check --- packages/kbn-api-contracts/allowlist.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/kbn-api-contracts/allowlist.json b/packages/kbn-api-contracts/allowlist.json index de213ad39bb82..916f53a8c1aab 100644 --- a/packages/kbn-api-contracts/allowlist.json +++ b/packages/kbn-api-contracts/allowlist.json @@ -9,6 +9,13 @@ "approvedBy": "@elastic/terraform-provider", "prUrl": "https://github.com/elastic/kibana/pull/258986" }, + { + "path": "/api/fleet/outputs/{outputId}", + "method": "put", + "reason": "PUT request body uses UpdateOutputSchema with meta IDs, changing inline anyOf members to $ref components. Data shape is identical; adds otel_exporter_config_yaml field.", + "approvedBy": "elastic/fleet", + "prUrl": "https://github.com/elastic/kibana/pull/259308" + }, { "path": "/api/fleet/agent_policies", "method": "get",