diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index 4875efac28e86b7..75558c1976f9eaf 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -19761,6 +19761,25 @@ components: collection will be disabled nullable: true type: boolean + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number monitoring_enabled: items: enum: @@ -19768,9 +19787,22 @@ components: - logs type: string type: array + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled monitoring_output_id: nullable: true type: string + monitoring_pprof_enabled: + type: boolean name: type: string namespace: @@ -20242,6 +20274,63 @@ components: type: string inputs: type: string + monitoring: + type: object + properties: + diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number + enabled: + type: boolean + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + logs: + type: boolean + metrics: + type: boolean + namespace: + type: string + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + traces: + type: boolean + use_output: + type: string + required: + - enabled + - metrics + - logs + - traces output_permissions: additionalProperties: type: object diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 3a5a6e76e4b307a..618a71ed974cc24 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -12634,6 +12634,25 @@ components: collection will be disabled nullable: true type: boolean + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number monitoring_enabled: items: enum: @@ -12641,9 +12660,22 @@ components: - logs type: string type: array + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled monitoring_output_id: nullable: true type: string + monitoring_pprof_enabled: + type: boolean name: type: string namespace: @@ -13115,6 +13147,63 @@ components: type: string inputs: type: string + monitoring: + type: object + properties: + diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number + enabled: + type: boolean + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + logs: + type: boolean + metrics: + type: boolean + namespace: + type: string + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + traces: + type: boolean + use_output: + type: string + required: + - enabled + - metrics + - logs + - traces output_permissions: additionalProperties: type: object diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index 872a5aaeaa12333..839c1908d54e98a 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -27556,6 +27556,25 @@ components: collection will be disabled nullable: true type: boolean + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number monitoring_enabled: items: enum: @@ -27563,9 +27582,22 @@ components: - logs type: string type: array + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled monitoring_output_id: nullable: true type: string + monitoring_pprof_enabled: + type: boolean name: type: string namespace: @@ -28037,6 +28069,63 @@ components: type: string inputs: type: string + monitoring: + type: object + properties: + diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number + enabled: + type: boolean + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + logs: + type: boolean + metrics: + type: boolean + namespace: + type: string + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + traces: + type: boolean + use_output: + type: string + required: + - enabled + - metrics + - logs + - traces output_permissions: additionalProperties: type: object diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 24623ca32869c0d..7aaf36b2c15c7fc 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -19593,6 +19593,25 @@ components: collection will be disabled nullable: true type: boolean + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number monitoring_enabled: items: enum: @@ -19600,9 +19619,22 @@ components: - logs type: string type: array + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled monitoring_output_id: nullable: true type: string + monitoring_pprof_enabled: + type: boolean name: type: string namespace: @@ -20074,6 +20106,63 @@ components: type: string inputs: type: string + monitoring: + type: object + properties: + diagnostics: + type: object + properties: + limit: + type: object + properties: + burst: + type: number + interval: + type: string + uploader: + type: object + properties: + init_dur: + type: string + max_dur: + type: string + max_retries: + type: number + enabled: + type: boolean + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + logs: + type: boolean + metrics: + type: boolean + namespace: + type: string + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + traces: + type: boolean + use_output: + type: string + required: + - enabled + - metrics + - logs + - traces output_permissions: additionalProperties: type: object diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index ec14f4519d34416..87bf8c710487112 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -605,8 +605,11 @@ "is_preconfigured", "is_protected", "keep_monitoring_alive", + "monitoring_diagnostics", "monitoring_enabled", + "monitoring_http", "monitoring_output_id", + "monitoring_pprof_enabled", "name", "namespace", "overrides", diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 2bdf6e75ad1cb93..7b315efcbcd4f09 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1996,13 +1996,25 @@ "keep_monitoring_alive": { "type": "boolean" }, + "monitoring_diagnostics": { + "index": false, + "type": "flattened" + }, "monitoring_enabled": { "index": false, "type": "keyword" }, + "monitoring_http": { + "index": false, + "type": "flattened" + }, "monitoring_output_id": { "type": "keyword" }, + "monitoring_pprof_enabled": { + "index": false, + "type": "boolean" + }, "name": { "type": "keyword" }, diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 2183100df95378b..7ba088c76bad8bc 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -879,6 +879,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D roleAndPrivileges: `${FLEET_DOCS}fleet-roles-and-privileges.html`, proxiesSettings: `${FLEET_DOCS}fleet-agent-proxy-support.html`, unprivilegedMode: `${FLEET_DOCS}elastic-agent-unprivileged.html#unprivileged-change-mode`, + httpMonitoring: `${FLEET_DOCS}agent-policy.html#change-policy-enable-agent-monitoring`, }, integrationDeveloper: { upload: `${INTEGRATIONS_DEV_DOCS}upload-a-new-integration.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index cbf085623c3a634..9870d3687e8b26d 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -564,6 +564,7 @@ export interface DocLinks { roleAndPrivileges: string; proxiesSettings: string; unprivilegedMode: string; + httpMonitoring: string; }>; readonly integrationDeveloper: { upload: string; 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 170cfa59587827d..7f69925edf4c046 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 @@ -120,7 +120,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infra-custom-dashboards": "1a5994f2e05bb8a1609825ddbf5012f77c5c67f3", "infrastructure-monitoring-log-view": "5f86709d3c27aed7a8379153b08ee5d3d90d77f5", "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", - "ingest-agent-policies": "90625b4a5ded9d4867358fcccc14a57c0454fcee", + "ingest-agent-policies": "5e95e539826a40ad08fd0c1d161da0a4d86ffc6d", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", "ingest-package-policies": "53a94064674835fdb35e5186233bcd7052eabd22", diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index aeef4ba323fa93e..e8ee2788c7632c2 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -7592,6 +7592,56 @@ }, "description": "User defined data tags that are added to all of the inputs. The values can be strings or numbers." } + }, + "monitoring_pprof_enabled": { + "type": "boolean" + }, + "monitoring_http": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "required": [ + "enabled" + ] + }, + "monitoring_diagnostics": { + "type": "object", + "properties": { + "limit": { + "type": "object", + "properties": { + "interval": { + "type": "string" + }, + "burst": { + "type": "number" + } + } + }, + "uploader": { + "type": "object", + "properties": { + "max_retries": { + "type": "number" + }, + "init_dur": { + "type": "string" + }, + "max_dur": { + "type": "string" + } + } + } + } } }, "required": [ @@ -7874,6 +7924,93 @@ } } }, + "monitoring": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "use_output": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "metrics": { + "type": "boolean" + }, + "logs": { + "type": "boolean" + }, + "traces": { + "type": "boolean" + }, + "pprof": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ] + }, + "http": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + } + }, + "required": [ + "enabled" + ] + }, + "diagnostics": { + "type": "object", + "properties": { + "limit": { + "type": "object", + "properties": { + "interval": { + "type": "string" + }, + "burst": { + "type": "number" + } + } + }, + "uploader": { + "type": "object", + "properties": { + "max_retries": { + "type": "number" + }, + "init_dur": { + "type": "string" + }, + "max_dur": { + "type": "string" + } + } + } + } + } + }, + "required": [ + "enabled", + "metrics", + "logs", + "traces" + ] + }, "fleet": { "oneOf": [ { diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index d5cb90624a19626..9e8291ee3ea1da2 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -4861,6 +4861,38 @@ components: description: >- User defined data tags that are added to all of the inputs. The values can be strings or numbers. + monitoring_pprof_enabled: + type: boolean + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + interval: + type: string + burst: + type: number + uploader: + type: object + properties: + max_retries: + type: number + init_dur: + type: string + max_dur: + type: string required: - id - status @@ -5054,6 +5086,63 @@ components: type: integer data: $ref: '#/components/schemas/full_agent_policy_output_permissions' + monitoring: + type: object + properties: + namespace: + type: string + use_output: + type: string + enabled: + type: boolean + metrics: + type: boolean + logs: + type: boolean + traces: + type: boolean + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + diagnostics: + type: object + properties: + limit: + type: object + properties: + interval: + type: string + burst: + type: number + uploader: + type: object + properties: + max_retries: + type: number + init_dur: + type: string + max_dur: + type: string + required: + - enabled + - metrics + - logs + - traces fleet: oneOf: - type: object diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml index 02fd5c2800a48ee..59f30f863c243b5 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent_policy.yaml @@ -87,6 +87,38 @@ properties: - type: string - type: number description: User defined data tags that are added to all of the inputs. The values can be strings or numbers. + monitoring_pprof_enabled: + type: boolean + monitoring_http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + monitoring_diagnostics: + type: object + properties: + limit: + type: object + properties: + interval: + type: string + burst: + type: number + uploader: + type: object + properties: + max_retries: + type: number + init_dur: + type: string + max_dur: + type: string required: - id - status diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml index afe7f1aec328ef9..c901a60f59e43d5 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/full_agent_policy.yaml @@ -16,6 +16,63 @@ properties: type: integer data: $ref: ./full_agent_policy_output_permissions.yaml + monitoring: + type: object + properties: + namespace: + type: string + use_output: + type: string + enabled: + type: boolean + metrics: + type: boolean + logs: + type: boolean + traces: + type: boolean + pprof: + type: object + properties: + enabled: + type: boolean + required: + - enabled + http: + type: object + properties: + enabled: + type: boolean + host: + type: string + port: + type: number + required: + - enabled + diagnostics: + type: object + properties: + limit: + type: object + properties: + interval: + type: string + burst: + type: number + uploader: + type: object + properties: + max_retries: + type: number + init_dur: + type: string + max_dur: + type: string + required: + - enabled + - metrics + - logs + - traces fleet: oneOf: - type: object diff --git a/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx index e1bea2103c63606..a4b41979840b246 100644 --- a/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx +++ b/x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx @@ -105,32 +105,6 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [ 'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings', schema: zodStringWithDurationValidation.default('30s'), }, - { - name: 'agent.monitoring.http', - api_field: { - name: 'agent_monitoring_http', - }, - title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpTitle', { - defaultMessage: 'HTTP monitoring endpoint', - }), - description: i18n.translate( - 'xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpDescription', - { - defaultMessage: - 'Enables a liveness HTTP endpoint that returns the overall health of Elastic Agent. This can be used by Kubernetes to restart the container, for example.', - } - ), - learnMoreLink: - 'https://www.elastic.co/guide/en/fleet/current/agent-policy.html#agent-policy-http-monitoring', - schema: z - .object({ - enabled: z.boolean().describe('Enabled').default(false), - host: z.string().describe('Host').default('localhost'), - port: z.number().describe('Port').min(0).max(65353).default(6791), - 'buffer.enabled': z.boolean().describe('Buffer Enabled').default(false), - }) - .default({}), - }, { name: 'agent.logging.level', title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle', { diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index e639b364343d926..72ba130e773799a 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -44,6 +44,23 @@ export interface NewAgentPolicy { keep_monitoring_alive?: boolean | null; supports_agentless?: boolean | null; global_data_tags?: GlobalDataTag[]; + monitoring_pprof_enabled?: boolean; + monitoring_http?: { + enabled: boolean; + host?: string; + port?: number; + }; + monitoring_diagnostics?: { + limit?: { + interval?: string; + burst?: number; + }; + uploader?: { + max_retries?: number; + init_dur?: string; + max_dur?: string; + }; + }; } export interface GlobalDataTag { @@ -119,6 +136,25 @@ export interface FullAgentPolicyMonitoring { metrics: boolean; logs: boolean; traces: boolean; + pprof?: { + enabled: boolean; + }; + http?: { + enabled: boolean; + host?: string; + port?: number; + }; + diagnostics?: { + limit?: { + interval?: string; + burst?: number; + }; + uploader?: { + max_retries?: number; + init_dur?: string; + max_dur?: string; + }; + }; } export interface FullAgentPolicy { diff --git a/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts b/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts index a6ec3b26c923268..d9edc61f7c0bf83 100644 --- a/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/a11y/home_page.cy.ts @@ -118,7 +118,7 @@ describe('Home page', () => { 'be.visible' ); cy.getBySel(AGENT_POLICY_CREATE_AGENT_POLICY_NAME_FIELD).type('testName'); - cy.get('.ingest-active-button').click(); + cy.getBySel(AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT.ADVANCED_OPTIONS_TOGGLE).click(); cy.getBySel(AGENT_POLICIES_FLYOUT_ADVANCED_DEFAULT_NAMESPACE_HEADER, { timeout: 15000, }).should('be.visible'); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index 4beadbd6a8fb065..4e1a0ac0f7e197a 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -95,6 +95,7 @@ export const AGENT_FLYOUT = { export const AGENT_POLICIES_CREATE_AGENT_POLICY_FLYOUT = { TITLE: 'createAgentPolicyFlyoutTitle', CREATE_BUTTON: 'createAgentPolicyButton', + ADVANCED_OPTIONS_TOGGLE: 'advancedOptionsButton', COLLECT_LOGS_CHECKBOX: 'collectLogsCheckbox', COLLECT_METRICS_CHECKBOX: 'collectMetricsCheckbox', }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.test.tsx index fa03e9a0fd6fe3a..8bafc124ec36b64 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.test.tsx @@ -102,70 +102,6 @@ describe('ConfiguredSettings', () => { expect(mockUpdateAdvancedSettingsHasErrors).toHaveBeenCalledWith(true); }); - it('should render field group', () => { - const result = render([ - { - name: 'agent.monitoring.http', - api_field: { - name: 'agent_monitoring_http', - }, - title: 'Agent HTTP monitoring', - description: 'Agent HTTP monitoring settings', - learnMoreLink: - 'https://www.elastic.co/guide/en/fleet/current/enable-custom-policy-settings.html#override-default-monitoring-port', - schema: z - .object({ - enabled: z.boolean().describe('Enabled').default(false), - host: z.string().describe('Host').default('localhost'), - port: z.number().describe('Port').min(0).max(65353).default(6791), - 'buffer.enabled': z.boolean().describe('Buffer Enabled').default(false), - }) - .default({}), - }, - ]); - - expect(result.getByText('Agent HTTP monitoring')).not.toBeNull(); - expect(result.getByText('Buffer Enabled')).not.toBeNull(); - const switches = result.getAllByRole('switch'); - expect(switches).toHaveLength(2); - expect(switches[0]).not.toBeChecked(); - expect(switches[1]).not.toBeChecked(); - const port = result.getByTestId('configuredSetting-agent.monitoring.http-port'); - expect(port).toHaveValue(6791); - const host = result.getByTestId('configuredSetting-agent.monitoring.http-host'); - expect(host).toHaveValue('localhost'); - - act(() => { - fireEvent.click(switches[0]); - }); - - expect(mockUpdateAgentPolicy).toHaveBeenCalledWith( - expect.objectContaining({ - advanced_settings: expect.objectContaining({ agent_monitoring_http: { enabled: true } }), - }) - ); - - act(() => { - fireEvent.change(port, { target: { value: '6792' } }); - }); - - expect(mockUpdateAgentPolicy).toHaveBeenCalledWith( - expect.objectContaining({ - advanced_settings: expect.objectContaining({ agent_monitoring_http: { port: 6792 } }), - }) - ); - - act(() => { - fireEvent.change(host, { target: { value: '1.2.3.4' } }); - }); - - expect(mockUpdateAgentPolicy).toHaveBeenCalledWith( - expect.objectContaining({ - advanced_settings: expect.objectContaining({ agent_monitoring_http: { host: '1.2.3.4' } }), - }) - ); - }); - it('should not render field if hidden', () => { const result = render([ { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/advanced_monitoring.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/advanced_monitoring.tsx new file mode 100644 index 000000000000000..de765531d7c6ae5 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/advanced_monitoring.tsx @@ -0,0 +1,535 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { + EuiDescribedFormGroup, + EuiSpacer, + EuiCheckbox, + EuiLink, + EuiAccordion, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldText, + EuiFieldNumber, +} from '@elastic/eui'; +import styled from 'styled-components'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import type { NewAgentPolicy, AgentPolicy } from '../../../../types'; +import { useStartServices } from '../../../../hooks'; +import type { ValidationResults } from '../agent_policy_validation'; + +const StyledEuiAccordion = styled(EuiAccordion)` + margin-block-start: ${(props) => props.theme.eui.euiSizeS}; + .ingest-active-button { + color: ${(props) => props.theme.eui.euiColorPrimary}; + } +`; + +const PushedDescribedFormGroup = styled(EuiDescribedFormGroup)` + h3, + .euiDescribedFormGroup__description { + padding-left: ${(props) => props.theme.eui.euiSizeL}; + } +`; + +export const AgentPolicyAdvancedMonitoringOptions: React.FunctionComponent<{ + agentPolicy: Partial; + disabled: boolean; + validation: ValidationResults; + touchedFields: { [key: string]: boolean }; + updateTouchedFields: (fields: { [key: string]: boolean }) => void; + updateAgentPolicy: (u: Partial) => void; +}> = ({ + agentPolicy, + disabled, + validation, + touchedFields, + updateTouchedFields, + updateAgentPolicy, +}) => { + const { docLinks } = useStartServices(); + + return ( + + } + buttonClassName={disabled ? undefined : 'ingest-active-button'} + isDisabled={disabled === true} + > + + + {/* HTTP monitoring endpoint */} + + + + } + description={ + + + + ), + }} + /> + } + > + + + {/* Enable base HTTP monitoring endpoint */} + /liveness, + }} + /> + } + disabled={disabled} + checked={agentPolicy.monitoring_http?.enabled} + onChange={(e) => { + const isEnabled = e.target.checked; + const host = isEnabled && !agentPolicy.monitoring_http?.host ? 'localhost' : undefined; + const port = isEnabled && !agentPolicy.monitoring_http?.port ? 6791 : undefined; + updateTouchedFields({ 'monitoring_http.enabled': true }); + updateAgentPolicy({ + monitoring_http: { + ...agentPolicy.monitoring_http, + ...(host ? { host } : {}), + ...(port ? { port } : {}), + enabled: isEnabled, + }, + }); + }} + /> + + + + {/* Host and port */} + + + + } + isDisabled={disabled || !agentPolicy.monitoring_http?.enabled} + error={ + touchedFields['monitoring_http.host'] && validation['monitoring_http.host'] + ? validation['monitoring_http.host'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_http.host'] && validation['monitoring_http.host'] + )} + > + { + updateAgentPolicy({ + monitoring_http: { + ...agentPolicy.monitoring_http!, + host: e.target.value, + }, + }); + }} + onBlur={() => updateTouchedFields({ 'monitoring_http.host': true })} + isInvalid={Boolean( + touchedFields['monitoring_http.host'] && validation['monitoring_http.host'] + )} + /> + + + + + } + isDisabled={disabled || !agentPolicy.monitoring_http?.enabled} + error={ + touchedFields['monitoring_http.port'] && validation['monitoring_http.port'] + ? validation['monitoring_http.port'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_http.port'] && validation['monitoring_http.port'] + )} + > + { + updateAgentPolicy({ + monitoring_http: { + ...agentPolicy.monitoring_http!, + port: e.target.value ? Number(e.target.value) : 0, + }, + }); + }} + onBlur={() => updateTouchedFields({ 'monitoring_http.port': true })} + isInvalid={Boolean( + touchedFields['monitoring_http.port'] && validation['monitoring_http.port'] + )} + /> + + + + + + + {/* Profiling endpoint */} + /debug/pprof, + }} + /> + } + disabled={disabled || !agentPolicy.monitoring_http?.enabled} + checked={agentPolicy.monitoring_pprof_enabled} + onChange={(e) => { + updateTouchedFields({ monitoring_pprof_enabled: true }); + updateAgentPolicy({ + monitoring_pprof_enabled: e.target.checked, + }); + }} + /> + + + {/* Diagnostics rate limiting */} + + + + } + description={ + + + + ), + }} + /> + } + > + + + + } + isDisabled={disabled} + error={ + touchedFields['monitoring_diagnostics.limit.interval'] && + validation['monitoring_diagnostics.limit.interval'] + ? validation['monitoring_diagnostics.limit.interval'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.limit.interval'] && + validation['monitoring_diagnostics.limit.interval'] + )} + > + { + updateAgentPolicy({ + monitoring_diagnostics: { + ...agentPolicy.monitoring_diagnostics, + limit: { + ...agentPolicy.monitoring_diagnostics?.limit, + interval: e.target.value ? e.target.value : undefined, + }, + }, + }); + }} + onBlur={() => + updateTouchedFields({ 'monitoring_diagnostics.limit.interval': true }) + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.limit.interval'] && + validation['monitoring_diagnostics.limit.interval'] + )} + /> + + + + + } + isDisabled={disabled} + error={ + touchedFields['monitoring_diagnostics.limit.burst'] && + validation['monitoring_diagnostics.limit.burst'] + ? validation['monitoring_diagnostics.limit.burst'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.limit.burst'] && + validation['monitoring_diagnostics.limit.burst'] + )} + > + { + updateAgentPolicy({ + monitoring_diagnostics: { + ...agentPolicy.monitoring_diagnostics, + limit: { + ...agentPolicy.monitoring_diagnostics?.limit, + burst: e.target.value ? Number(e.target.value) : undefined, + }, + }, + }); + }} + onBlur={() => updateTouchedFields({ 'monitoring_diagnostics.limit.burst': true })} + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.limit.burst'] && + validation['monitoring_diagnostics.limit.burst'] + )} + /> + + + {/* Empty to match colums with upload fields below */} + + + + {/* Diagnostics file upload */} + + + + } + description={ + + + + ), + }} + /> + } + > + + + + } + isDisabled={disabled} + error={ + touchedFields['monitoring_diagnostics.uploader.max_retries'] && + validation['monitoring_diagnostics.uploader.max_retries'] + ? validation['monitoring_diagnostics.uploader.max_retries'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.max_retries'] && + validation['monitoring_diagnostics.uploader.max_retries'] + )} + > + { + updateAgentPolicy({ + monitoring_diagnostics: { + ...agentPolicy.monitoring_diagnostics, + uploader: { + ...agentPolicy.monitoring_diagnostics?.uploader, + max_retries: e.target.value ? Number(e.target.value) : undefined, + }, + }, + }); + }} + onBlur={() => + updateTouchedFields({ 'monitoring_diagnostics.uploader.max_retries': true }) + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.max_retries'] && + validation['monitoring_diagnostics.uploader.max_retries'] + )} + /> + + + + + } + isDisabled={disabled} + error={ + touchedFields['monitoring_diagnostics.uploader.init_dur'] && + validation['monitoring_diagnostics.uploader.init_dur'] + ? validation['monitoring_diagnostics.uploader.init_dur'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.init_dur'] && + validation['monitoring_diagnostics.uploader.init_dur'] + )} + > + { + updateAgentPolicy({ + monitoring_diagnostics: { + ...agentPolicy.monitoring_diagnostics, + uploader: { + ...agentPolicy.monitoring_diagnostics?.uploader, + init_dur: e.target.value ? e.target.value : undefined, + }, + }, + }); + }} + onBlur={() => + updateTouchedFields({ 'monitoring_diagnostics.uploader.init_dur': true }) + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.init_dur'] && + validation['monitoring_diagnostics.uploader.init_dur'] + )} + /> + + + + + } + isDisabled={disabled} + error={ + touchedFields['monitoring_diagnostics.uploader.max_dur'] && + validation['monitoring_diagnostics.uploader.max_dur'] + ? validation['monitoring_diagnostics.uploader.max_dur'] + : null + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.max_dur'] && + validation['monitoring_diagnostics.uploader.max_dur'] + )} + > + { + updateAgentPolicy({ + monitoring_diagnostics: { + ...agentPolicy.monitoring_diagnostics, + uploader: { + ...agentPolicy.monitoring_diagnostics?.uploader, + max_dur: e.target.value ? e.target.value : undefined, + }, + }, + }); + }} + onBlur={() => + updateTouchedFields({ 'monitoring_diagnostics.uploader.max_dur': true }) + } + isInvalid={Boolean( + touchedFields['monitoring_diagnostics.uploader.max_dur'] && + validation['monitoring_diagnostics.uploader.max_dur'] + )} + /> + + + + + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 9e35dc441fa28d4..fc16d56107ccd1f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -62,6 +62,7 @@ import { import { CustomFields } from './custom_fields'; import { SpaceSelector } from './space_selector'; +import { AgentPolicyAdvancedMonitoringOptions } from './advanced_monitoring'; interface Props { agentPolicy: Partial; @@ -141,6 +142,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = /> } > + @@ -401,6 +403,7 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = /> } > + = }} /> + + setTouchedFields({ ...touchedFields, ...fields })} + updateAgentPolicy={updateAgentPolicy} + /> + + {AgentTamperProtectionSection} = ({ { it('should return error when agentPolicy has empty namespace', () => { const result = agentPolicyFormValidation({ - namespace: 'Default', + namespace: '', name: 'policy', }); expect(result.namespace).toBeDefined(); @@ -33,18 +33,113 @@ describe('Agent Policy form validation', () => { it('should return error when agentPolicy has negative unenroll timeout', () => { const result = agentPolicyFormValidation({ - namespace: 'Default', + namespace: 'default', name: 'policy', unenroll_timeout: -1, }); expect(result.unenroll_timeout).toBeDefined(); }); + it('should return error when agentPolicy has negative inactivity timeout', () => { const result = agentPolicyFormValidation({ - namespace: 'Default', + namespace: 'default', name: 'policy', inactivity_timeout: -1, }); expect(result.inactivity_timeout).toBeDefined(); }); + + it('should return error when agentPolicy has http monitoring enabled without host or port', () => { + expect( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_http: { + enabled: true, + host: 'localhost', + port: 123, + }, + }) + ).toEqual({}); + + expect( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_http: { + enabled: false, + host: '', + }, + }) + ).toEqual({}); + + expect( + Object.keys( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_http: { + enabled: true, + host: '', + port: 123, + }, + }) + ) + ).toEqual(['monitoring_http.host']); + + expect( + Object.keys( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_http: { + enabled: true, + host: '', + }, + }) + ) + ).toEqual(['monitoring_http.host', 'monitoring_http.port']); + }); + + it('should return error when agentPolicy has invalid diagnostics options', () => { + expect( + Object.keys( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_diagnostics: { + limit: { + burst: 0, + }, + uploader: { + max_retries: -1, + }, + }, + }) + ) + ).toEqual([ + 'monitoring_diagnostics.limit.burst', + 'monitoring_diagnostics.uploader.max_retries', + ]); + + expect( + Object.keys( + agentPolicyFormValidation({ + namespace: 'default', + name: 'policy', + monitoring_diagnostics: { + limit: { + burst: -1, + }, + uploader: { + max_retries: 0, + }, + }, + }) + ) + ).toEqual([ + 'monitoring_diagnostics.limit.burst', + 'monitoring_diagnostics.uploader.max_retries', + ]); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx index a52c2b488139c7f..109e62b0e0de42e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_validation.tsx @@ -42,7 +42,7 @@ export const agentPolicyFormValidation = ( errors.unenroll_timeout = [ , ]; } @@ -51,7 +51,54 @@ export const agentPolicyFormValidation = ( errors.inactivity_timeout = [ , + ]; + } + + if (agentPolicy.monitoring_http?.enabled) { + if (!agentPolicy.monitoring_http.host?.trim()) { + errors['monitoring_http.host'] = [ + , + ]; + } + + if ( + !agentPolicy.monitoring_http.port || + (agentPolicy.monitoring_http.port !== undefined && agentPolicy.monitoring_http.port <= 0) + ) { + errors['monitoring_http.port'] = [ + , + ]; + } + } + + if ( + agentPolicy.monitoring_diagnostics?.limit?.burst !== undefined && + agentPolicy.monitoring_diagnostics?.limit?.burst <= 0 + ) { + errors['monitoring_diagnostics.limit.burst'] = [ + , + ]; + } + + if ( + agentPolicy.monitoring_diagnostics?.uploader?.max_retries !== undefined && + agentPolicy.monitoring_diagnostics?.uploader?.max_retries <= 0 + ) { + errors['monitoring_diagnostics.uploader.max_retries'] = [ + , ]; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx index 9be265a6385cbb7..14dcf6df21b9fb0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/settings/index.tsx @@ -57,6 +57,9 @@ const pickAgentPolicyKeysToSend = (agentPolicy: AgentPolicy) => 'is_protected', 'advanced_settings', 'global_data_tags', + 'monitoring_pprof_enabled', + 'monitoring_http', + 'monitoring_diagnostics', ]); const FormWrapper = styled.div` diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 3c224ee6ba88121..0eb6c86df01e2d2 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -91,7 +91,7 @@ import { migratePackagePolicyToV81102, migratePackagePolicyEvictionsFromV81102, } from './migrations/security_solution/to_v8_11_0_2'; -import { settingsV1 } from './model_versions/v1'; +import { settingsV1 } from './model_versions/settings_v1'; import { packagePolicyV13AdvancedFields, packagePolicyV10OnWriteScanFix, @@ -100,6 +100,7 @@ import { migratePackagePolicyIdsToV8150, migratePackagePolicySetRequiresRootToV8150, } from './migrations/to_v8_15_0'; +import { backfillAgentPolicyToV4 } from './model_versions/agent_policy_v4'; /* * Saved object types and mappings @@ -223,6 +224,9 @@ export const getSavedObjectTypes = ( advanced_settings: { type: 'flattened', index: false }, supports_agentless: { type: 'boolean' }, global_data_tags: { type: 'flattened', index: false }, + monitoring_pprof_enabled: { type: 'boolean', index: false }, + monitoring_http: { type: 'flattened', index: false }, + monitoring_diagnostics: { type: 'flattened', index: false }, }, }, migrations: { @@ -263,6 +267,22 @@ export const getSavedObjectTypes = ( }, ], }, + '4': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + monitoring_pprof_enabled: { type: 'boolean', index: false }, + monitoring_http: { type: 'flattened', index: false }, + monitoring_diagnostics: { type: 'flattened', index: false }, + }, + }, + { + type: 'data_backfill', + backfillFn: backfillAgentPolicyToV4, + }, + ], + }, }, }, [AGENT_POLICY_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.test.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.test.ts new file mode 100644 index 000000000000000..0dcd883168df24e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SavedObject, + SavedObjectModelTransformationContext, +} from '@kbn/core-saved-objects-server'; + +import type { AgentPolicy } from '../../../common'; + +import { backfillAgentPolicyToV4 } from './agent_policy_v4'; + +describe('migrateAgentPolicyToV8160', () => { + it('should migrate advanced_settings.agent_monitoring_http to monitoring_http', () => { + const agentPolicyDoc: SavedObject = { + id: 'policy1', + type: 'ingest-agent-policies', + references: [], + attributes: { + id: 'policy1', + name: 'Policy 1', + namespace: 'default', + advanced_settings: { + agent_monitoring_http: { + enabled: true, + host: 'localhost', + port: 1111, + 'buffer.enabled': true, + }, + }, + is_managed: false, + status: 'active', + updated_at: '2021-08-17T14:00:00.000Z', + updated_by: 'elastic', + revision: 1, + is_protected: false, + }, + }; + + const migratedAgentPolicyDoc = backfillAgentPolicyToV4( + agentPolicyDoc, + {} as SavedObjectModelTransformationContext + ); + + expect(migratedAgentPolicyDoc.attributes.monitoring_http).toEqual({ + enabled: true, + host: 'localhost', + port: 1111, + }); + expect( + migratedAgentPolicyDoc.attributes.advanced_settings?.agent_monitoring_http + ).toBeUndefined(); + }); + + it('should migrate advanced_settings.agent_monitoring_http to monitoring_http when most values are missing', () => { + const agentPolicyDoc: SavedObject = { + id: 'policy1', + type: 'ingest-agent-policies', + references: [], + attributes: { + id: 'policy1', + name: 'Policy 1', + namespace: 'default', + advanced_settings: { + agent_monitoring_http: { + enabled: true, + }, + }, + is_managed: false, + status: 'active', + updated_at: '2021-08-17T14:00:00.000Z', + updated_by: 'elastic', + revision: 1, + is_protected: false, + }, + }; + + const migratedAgentPolicyDoc = backfillAgentPolicyToV4( + agentPolicyDoc, + {} as SavedObjectModelTransformationContext + ); + + expect(migratedAgentPolicyDoc.attributes.monitoring_http).toEqual({ + enabled: true, + }); + expect( + migratedAgentPolicyDoc.attributes.advanced_settings?.agent_monitoring_http + ).toBeUndefined(); + }); + + it('should migrate advanced_settings.agent_monitoring_http to monitoring_http when some values are missing', () => { + const agentPolicyDoc: SavedObject = { + id: 'policy1', + type: 'ingest-agent-policies', + references: [], + attributes: { + id: 'policy1', + name: 'Policy 1', + namespace: 'default', + advanced_settings: { + agent_monitoring_http: { + enabled: true, + 'buffer.enabled': true, + }, + }, + is_managed: false, + status: 'active', + updated_at: '2021-08-17T14:00:00.000Z', + updated_by: 'elastic', + revision: 1, + is_protected: false, + }, + }; + + const migratedAgentPolicyDoc = backfillAgentPolicyToV4( + agentPolicyDoc, + {} as SavedObjectModelTransformationContext + ); + + expect(migratedAgentPolicyDoc.attributes.monitoring_http).toEqual({ + enabled: true, + }); + expect( + migratedAgentPolicyDoc.attributes.advanced_settings?.agent_monitoring_http + ).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.ts new file mode 100644 index 000000000000000..aa1b6624ab719c4 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/agent_policy_v4.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectModelDataBackfillFn } from '@kbn/core-saved-objects-server'; + +import type { AgentPolicy } from '../../../common'; + +export const backfillAgentPolicyToV4: SavedObjectModelDataBackfillFn = ( + agentPolicyDoc +) => { + const advancedSettings = agentPolicyDoc.attributes.advanced_settings; + if (advancedSettings?.agent_monitoring_http) { + agentPolicyDoc.attributes.monitoring_http = { + enabled: advancedSettings.agent_monitoring_http.enabled, + host: advancedSettings.agent_monitoring_http.host, + port: advancedSettings.agent_monitoring_http.port, + }; + delete advancedSettings.agent_monitoring_http; + } + return agentPolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/v1.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/settings_v1.ts similarity index 100% rename from x-pack/plugins/fleet/server/saved_objects/model_versions/v1.ts rename to x-pack/plugins/fleet/server/saved_objects/model_versions/settings_v1.ts diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index d2ff49b04e34020..fa5522d50802bd8 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -20,6 +20,7 @@ import { getPackageInfo } from '../epm/packages'; import { generateFleetConfig, getFullAgentPolicy, + getFullMonitoringSettings, transformOutputToFullPolicyOutput, } from './full_agent_policy'; import { getMonitoringPermissions } from './monitoring_permissions'; @@ -901,6 +902,167 @@ describe('getFullAgentPolicy', () => { }); }); +describe('getFullMonitoringSettings', () => { + it('should return the correct settings when all values are present', async () => { + const monitoringSettings = getFullMonitoringSettings( + { + namespace: 'default', + monitoring_enabled: ['metrics', 'logs', 'traces'], + monitoring_pprof_enabled: true, + monitoring_http: { + enabled: true, + host: 'localhost', + port: 1111, + }, + monitoring_diagnostics: { + limit: { + interval: '1m', + burst: 10, + }, + uploader: { + max_retries: 3, + init_dur: '1m', + max_dur: '10m', + }, + }, + }, + { + id: 'some-output', + is_default: false, + type: 'elasticsearch', + } + ); + + expect(monitoringSettings).toEqual({ + enabled: true, + logs: true, + metrics: true, + traces: true, + namespace: 'default', + use_output: 'some-output', + pprof: { enabled: true }, + http: { + enabled: true, + host: 'localhost', + port: 1111, + }, + diagnostics: { + limit: { + interval: '1m', + burst: 10, + }, + uploader: { + max_retries: 3, + init_dur: '1m', + max_dur: '10m', + }, + }, + }); + }); + + it('should return the correct settings when some values are present', async () => { + const monitoringSettings = getFullMonitoringSettings( + { + namespace: 'default', + monitoring_enabled: ['metrics'], + monitoring_pprof_enabled: false, + monitoring_http: { + enabled: true, + host: 'localhost', + }, + monitoring_diagnostics: { + limit: { + interval: '1m', + }, + uploader: { + max_dur: '10m', + }, + }, + }, + { + id: 'some-output', + is_default: true, + type: 'elasticsearch', + } + ); + + expect(monitoringSettings).toEqual({ + enabled: true, + logs: false, + metrics: true, + traces: false, + namespace: 'default', + use_output: 'default', + pprof: { enabled: false }, + http: { + enabled: true, + host: 'localhost', + }, + diagnostics: { + limit: { + interval: '1m', + }, + uploader: { + max_dur: '10m', + }, + }, + }); + }); + + it('should return the correct settings when beats monitoring is disabled and minimal values are present', async () => { + const monitoringSettings = getFullMonitoringSettings( + { + namespace: 'default', + monitoring_enabled: [], + monitoring_http: { + enabled: true, + }, + monitoring_diagnostics: {}, + }, + { + id: 'some-output', + is_default: true, + type: 'elasticsearch', + } + ); + + expect(monitoringSettings).toEqual({ + enabled: true, + logs: false, + metrics: false, + traces: false, + http: { + enabled: true, + }, + }); + }); + + it('should disable monitoring if beats and http monitoring are disabled', async () => { + const monitoringSettings = getFullMonitoringSettings( + { + namespace: 'default', + monitoring_enabled: [], + monitoring_http: { + enabled: false, + }, + monitoring_diagnostics: {}, + }, + { + id: 'some-output', + is_default: true, + type: 'elasticsearch', + } + ); + + expect(monitoringSettings).toEqual({ + enabled: false, + logs: false, + metrics: false, + traces: false, + }); + }); +}); + describe('transformOutputToFullPolicyOutput', () => { it('should works with only required field on a output', () => { const policyOutput = transformOutputToFullPolicyOutput({ diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index d00721f08a3d95b..05e963a4088e40c 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -136,36 +136,6 @@ export async function getFullAgentPolicy( const packagePolicySecretReferences = (agentPolicy?.package_policies || []).flatMap( (policy) => policy.secret_references || [] ); - const defaultMonitoringConfig: FullAgentPolicyMonitoring = { - enabled: false, - logs: false, - metrics: false, - traces: false, - }; - - let monitoring: FullAgentPolicyMonitoring = { ...defaultMonitoringConfig }; - - // If the agent policy has monitoring enabled for at least one of "logs", "metrics", or "traces" - // generate a monitoring config for the resulting compiled agent policy - if (agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0) { - monitoring = { - namespace: agentPolicy.namespace, - use_output: getOutputIdForAgentPolicy(monitoringOutput), - enabled: true, - logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), - metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), - traces: agentPolicy.monitoring_enabled.includes(dataTypes.Traces), - }; - // If the `keep_monitoring_alive` flag is set, enable monitoring but don't enable logs or metrics. - // This allows cloud or other environments to keep the monitoring server alive without tearing it down. - } else if (agentPolicy.keep_monitoring_alive) { - monitoring = { - enabled: true, - logs: false, - metrics: false, - traces: false, - }; - } const fullAgentPolicy: FullAgentPolicy = { id: agentPolicy.id, @@ -188,7 +158,7 @@ export async function getFullAgentPolicy( sourceURI: downloadSourceUri, ...(downloadSourceProxyUri ? { proxy_url: downloadSourceProxyUri } : {}), }, - monitoring, + monitoring: getFullMonitoringSettings(agentPolicy, monitoringOutput), features, protection: { enabled: agentPolicy.is_protected, @@ -550,11 +520,102 @@ export function transformOutputToFullPolicyOutput( return newOutput; } +export function getFullMonitoringSettings( + agentPolicy: Pick< + AgentPolicy, + | 'namespace' + | 'monitoring_enabled' + | 'keep_monitoring_alive' + | 'monitoring_pprof_enabled' + | 'monitoring_http' + | 'monitoring_diagnostics' + >, + monitoringOutput: Pick +): FullAgentPolicyMonitoring { + // Set base beats monitoring settings + const monitoring: FullAgentPolicyMonitoring = { + enabled: Boolean( + (agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0) || + agentPolicy.monitoring_http?.enabled || + agentPolicy.keep_monitoring_alive + ), + logs: false, + metrics: false, + traces: false, + }; + + // If the agent policy has monitoring enabled for at least one of "logs", "metrics", or "traces" + // generate a monitoring config for the resulting compiled agent policy + if (agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0) { + monitoring.namespace = agentPolicy.namespace; + monitoring.use_output = getOutputIdForAgentPolicy(monitoringOutput); + monitoring.logs = agentPolicy.monitoring_enabled.includes(dataTypes.Logs); + monitoring.metrics = agentPolicy.monitoring_enabled.includes(dataTypes.Metrics); + monitoring.traces = agentPolicy.monitoring_enabled.includes(dataTypes.Traces); + } + + if (agentPolicy.monitoring_pprof_enabled !== undefined) { + monitoring.pprof = { + enabled: agentPolicy.monitoring_pprof_enabled, + }; + } + + // Conditionally set http monitoring settings + if (agentPolicy.monitoring_http?.enabled) { + monitoring.http = { + enabled: agentPolicy.monitoring_http.enabled, + ...(agentPolicy.monitoring_http.host && { host: agentPolicy.monitoring_http.host }), + ...(agentPolicy.monitoring_http.port && { port: agentPolicy.monitoring_http.port }), + }; + } + + // Conditionally set diagnostics monitoring settings + if (agentPolicy.monitoring_diagnostics?.limit || agentPolicy.monitoring_diagnostics?.uploader) { + monitoring.diagnostics = {}; + + if ( + agentPolicy.monitoring_diagnostics.limit && + (agentPolicy.monitoring_diagnostics.limit.interval || + typeof agentPolicy.monitoring_diagnostics.limit.burst === 'number') + ) { + monitoring.diagnostics.limit = { + ...(agentPolicy.monitoring_diagnostics.limit.interval && { + interval: agentPolicy.monitoring_diagnostics.limit.interval, + }), + ...(typeof agentPolicy.monitoring_diagnostics.limit.burst === 'number' && { + burst: agentPolicy.monitoring_diagnostics.limit.burst, + }), + }; + } + + if ( + agentPolicy.monitoring_diagnostics.uploader && + (typeof agentPolicy.monitoring_diagnostics.uploader.max_retries === 'number' || + agentPolicy.monitoring_diagnostics.uploader.init_dur || + agentPolicy.monitoring_diagnostics.uploader.max_dur) + ) { + monitoring.diagnostics.uploader = { + ...(typeof agentPolicy.monitoring_diagnostics.uploader.max_retries === 'number' && { + max_retries: agentPolicy.monitoring_diagnostics.uploader.max_retries, + }), + ...(agentPolicy.monitoring_diagnostics.uploader.init_dur && { + init_dur: agentPolicy.monitoring_diagnostics.uploader.init_dur, + }), + ...(agentPolicy.monitoring_diagnostics.uploader.max_dur && { + max_dur: agentPolicy.monitoring_diagnostics.uploader.max_dur, + }), + }; + } + } + + return monitoring; +} + /** * Get id used in full agent policy (sent to the agents) * we use "default" for the default policy to avoid breaking changes */ -function getOutputIdForAgentPolicy(output: Output) { +function getOutputIdForAgentPolicy(output: Pick) { if (output.is_default && output.type === outputType.Elasticsearch) { return DEFAULT_OUTPUT.name; } diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 999448f7cae4756..dc856820695a29b 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -759,6 +759,9 @@ class AgentPolicyService { 'fleet_server_host_id', 'supports_agentless', 'global_data_tags', + 'monitoring_pprof_enabled', + 'monitoring_http', + 'monitoring_diagnostics', ]), ...newAgentPolicyProps, }, diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 560f6939eedba24..38441b06eabbfa9 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -102,6 +102,32 @@ export const AgentPolicyBaseSchema = { } ) ), + monitoring_pprof_enabled: schema.maybe(schema.boolean()), + monitoring_http: schema.maybe( + schema.object({ + enabled: schema.boolean(), + host: schema.maybe(schema.string({ defaultValue: 'localhost' })), + port: schema.maybe(schema.number({ min: 0, max: 65353, defaultValue: 6791 })), + buffer: schema.maybe(schema.object({ enabled: schema.boolean({ defaultValue: false }) })), + }) + ), + monitoring_diagnostics: schema.maybe( + schema.object({ + limit: schema.maybe( + schema.object({ + interval: schema.maybe(schema.string()), + burst: schema.maybe(schema.number()), + }) + ), + uploader: schema.maybe( + schema.object({ + max_retries: schema.maybe(schema.number()), + init_dur: schema.maybe(schema.string()), + max_dur: schema.maybe(schema.string()), + }) + ), + }) + ), }; function validateGlobalDataTagInput(tags: GlobalDataTag[]): string | undefined { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index e657bc24ddd3365..3903fba64200c77 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -20371,8 +20371,6 @@ "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle": "Niveau de logging de l'agent", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodDescription": "Fréquence de logging des indicateurs internes d'Elastic Agent.", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodTitle": "Période de mesure de logging de l'agent", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpDescription": "Active un point de terminaison HTTP actif qui renvoie l'état d’intégrité global d'Elastic Agent. Il peut par exemple être utilisé par Kubernetes pour redémarrer le conteneur.", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpTitle": "Point de terminaison de surveillance HTTP", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutDescription": "Le délai d'expiration pour le téléchargement du binaire des agents.", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutTitle": "Délai d'expiration de téléchargement du binaire des agents", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutValidationMessage": "Doit être une chaîne avec une unité de temps, par exemple 30 s, 5 m, 2 h, 1 d", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 95ac436c88217cc..3931961c383bae5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20361,8 +20361,6 @@ "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle": "エージェントログレベル", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodDescription": "内部Elasticエージェントメトリックをログに記録する頻度。", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodTitle": "エージェントログメトリック期間", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpDescription": "Elasticエージェントの全体的な正常性を返すライブネスHTTPエンドポイントを有効化します。これは、たとえば、コンテナーを再起動するためにKubernetesによって使用されることがあります。", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpTitle": "HTTP監視エンドポイント", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutDescription": "エージェントバイナリのダウンロードに関するタイムアウト。", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutTitle": "エージェントバイナリダウンロードタイムアウト", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutValidationMessage": "30s、5m、2h、1dなどの時間単位の文字列でなければなりません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 144b069e0e4f9e6..fcb7a9821fdba04 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20388,8 +20388,6 @@ "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle": "代理日志记录级别", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodDescription": "记录内部 Elastic 代理指标的频率。", "xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodTitle": "代理日志记录指标期间", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpDescription": "启用返回 Elastic 代理的整体运行状况的活性 HTTP 终端。例如,这可由 Kubernetes 用于重新启动容器。", - "xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpTitle": "HTTP 监测终端", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutDescription": "下载代理二进制文件时的超时值。", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutTitle": "代理二进制文件下载超时", "xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutValidationMessage": "必须为带时间单位的字符串,如 30s、5m、2h、1d", diff --git a/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts index 2bc3f6886700239..f601743e394ec36 100644 --- a/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts +++ b/x-pack/test/fleet_api_integration/apis/agent_policy/agent_policy.ts @@ -532,6 +532,86 @@ export default function (providerContext: FtrProviderContext) { }); } }); + + it('should create policy with advanced monitoring options', async () => { + const { + body: { item: createdPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies?sys_monitoring=true`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'advanced monitoring test', + namespace: 'default', + monitoring_pprof_enabled: true, + monitoring_http: { + host: 'localhost', + port: 6791, + enabled: true, + }, + monitoring_diagnostics: { + limit: { + interval: '1m', + burst: 1, + }, + uploader: { + max_retries: 10, + init_dur: '1s', + max_dur: '10m', + }, + }, + }) + .expect(200); + + const policyResponse = await supertest + .get(`/api/fleet/agent_policies/${createdPolicy.id}`) + .expect(200); + expect(policyResponse.body.item.monitoring_pprof_enabled).to.eql(true); + expect(policyResponse.body.item.monitoring_http).to.eql({ + host: 'localhost', + port: 6791, + enabled: true, + }); + expect(policyResponse.body.item.monitoring_diagnostics).to.eql({ + limit: { + interval: '1m', + burst: 1, + }, + uploader: { + max_retries: 10, + init_dur: '1s', + max_dur: '10m', + }, + }); + + const fullPolicyResponse = await supertest + .get(`/api/fleet/agent_policies/${createdPolicy.id}/full`) + .expect(200); + expect(fullPolicyResponse.body.item.agent.monitoring).to.eql({ + enabled: true, + logs: false, + metrics: false, + traces: false, + pprof: { + enabled: true, + }, + http: { + enabled: true, + host: 'localhost', + port: 6791, + }, + diagnostics: { + limit: { + interval: '1m', + burst: 1, + }, + uploader: { + max_retries: 10, + init_dur: '1s', + max_dur: '10m', + }, + }, + }); + }); }); describe('POST /api/fleet/agent_policies/{agentPolicyId}/copy', () => { @@ -978,6 +1058,65 @@ export default function (providerContext: FtrProviderContext) { expect(newPolicy.global_data_tags).to.eql([{ name: 'testName', value: 'testValue' }]); }); + + it('should copy advanced monitoring options', async () => { + const { + body: { item: policyWithAdvancedMonitoring }, + } = await supertest + .post(`/api/fleet/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'advanced monitoring test', + namespace: 'default', + monitoring_pprof_enabled: true, + monitoring_http: { + host: 'localhost', + port: 6791, + enabled: true, + }, + monitoring_diagnostics: { + limit: { + interval: '1m', + burst: 1, + }, + uploader: { + max_retries: 10, + init_dur: '1s', + max_dur: '10m', + }, + }, + }) + .expect(200); + + const { + body: { item: newPolicy }, + } = await supertest + .post(`/api/fleet/agent_policies/${policyWithAdvancedMonitoring.id}/copy`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'advanced monitoring test copy', + description: 'Test', + }) + .expect(200); + + expect(newPolicy.monitoring_pprof_enabled).to.eql(true); + expect(newPolicy.monitoring_http).to.eql({ + host: 'localhost', + port: 6791, + enabled: true, + }); + expect(newPolicy.monitoring_diagnostics).to.eql({ + limit: { + interval: '1m', + burst: 1, + }, + uploader: { + max_retries: 10, + init_dur: '1s', + max_dur: '10m', + }, + }); + }); }); describe('PUT /api/fleet/agent_policies/{agentPolicyId}', () => {