diff --git a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts b/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
index f0d7a84d36d2a..ac5c57f9107c5 100644
--- a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
+++ b/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
@@ -37,4 +37,93 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
},
schema: z.number().int().min(0).default(0),
},
+ {
+ name: 'agent.download.timeout',
+ hidden: true,
+ title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutTitle', {
+ defaultMessage: 'Agent binary download timeout',
+ }),
+ description: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.downloadTimeoutDescription',
+ {
+ defaultMessage: 'Timeout for downloading the agent binary',
+ }
+ ),
+ learnMoreLink:
+ 'https://www.elastic.co/guide/en/fleet/current/enable-custom-policy-settings.html#configure-agent-download-timeout',
+ api_field: {
+ name: 'agent_download_timeout',
+ },
+ schema: zodStringWithDurationValidation.default('2h'),
+ },
+ {
+ name: 'agent.download.target_directory',
+ hidden: true,
+ api_field: {
+ name: 'agent_download_target_directory',
+ },
+ title: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentDownloadTargetDirectoryTitle',
+ {
+ defaultMessage: 'Agent binary target directory',
+ }
+ ),
+ description: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentDownloadTargetDirectoryDescription',
+ {
+ defaultMessage: 'The disk path to which the agent binary will be downloaded',
+ }
+ ),
+ learnMoreLink:
+ 'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-download.html',
+ schema: z.string(),
+ },
+ {
+ name: 'agent.logging.metrics.period',
+ hidden: true,
+ api_field: {
+ name: 'agent_logging_metrics_period',
+ },
+ title: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodTitle',
+ {
+ defaultMessage: 'Agent logging metrics period',
+ }
+ ),
+ description: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentLoggingMetricsPeriodDescription',
+ {
+ defaultMessage: 'The frequency of agent metrics logging',
+ }
+ ),
+ learnMoreLink:
+ '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',
+ hidden: true,
+ api_field: {
+ name: 'agent_monitoring_http',
+ },
+ title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpTitle', {
+ defaultMessage: 'Agent HTTP monitoring',
+ }),
+ description: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentMonitoringHttpDescription',
+ {
+ defaultMessage: '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({}),
+ },
];
diff --git a/x-pack/plugins/fleet/common/settings/types.ts b/x-pack/plugins/fleet/common/settings/types.ts
index 0fc8ad98f17c0..da22e15dc2609 100644
--- a/x-pack/plugins/fleet/common/settings/types.ts
+++ b/x-pack/plugins/fleet/common/settings/types.ts
@@ -18,4 +18,5 @@ export interface SettingsConfig {
api_field: {
name: string;
};
+ hidden?: boolean;
}
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 7d0cb7dbae341..2631a6527987f 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
@@ -101,4 +101,86 @@ describe('ConfiguredSettings', () => {
).not.toBeNull();
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([
+ {
+ name: 'agent.limits.go_max_procs',
+ hidden: true,
+ title: 'GO_MAX_PROCS',
+ description: 'Description',
+ learnMoreLink: '',
+ api_field: {
+ name: 'agent_limits_go_max_procs',
+ },
+ schema: z.number().int().min(0).default(0),
+ },
+ ]);
+
+ expect(result.queryByText('GO_MAX_PROCS')).toBeNull();
+ });
});
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
index 03d66d23615ba..2836f1a2c8c38 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
@@ -5,24 +5,24 @@
* 2.0.
*/
-import { z, ZodFirstPartyTypeKind } from 'zod';
-import React, { useState } from 'react';
-import {
- EuiDescribedFormGroup,
- EuiFieldNumber,
- EuiFieldText,
- EuiFormRow,
- EuiLink,
-} from '@elastic/eui';
+import { ZodFirstPartyTypeKind } from 'zod';
+import React from 'react';
+import { EuiFieldNumber, EuiFieldText } from '@elastic/eui';
import type { SettingsConfig } from '../../../../../common/settings/types';
-import { useAgentPolicyFormContext } from '../../sections/agent_policy/components/agent_policy_form';
+
+import { SettingsFieldGroup } from './settings_field_group';
+import { getInnerType, SettingsFieldWrapper } from './settings_field_wrapper';
export const settingComponentRegistry = new Map<
string,
(settingsconfig: SettingsConfig) => React.ReactElement
>();
+settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodObject, (settingsConfig) => (
+
+));
+
settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodNumber, (settingsConfig) => {
return (
= ({ settingsConfig, typeName, renderItem }) => {
- const [error, setError] = useState('');
- const agentPolicyFormContext = useAgentPolicyFormContext();
-
- const fieldKey = `configuredSetting-${settingsConfig.name}`;
- const defaultValue: number =
- settingsConfig.schema instanceof z.ZodDefault
- ? settingsConfig.schema._def.defaultValue()
- : undefined;
- const coercedSchema = settingsConfig.schema as z.ZodString;
-
- const convertValue = (value: string, type: keyof typeof ZodFirstPartyTypeKind): any => {
- if (type === ZodFirstPartyTypeKind.ZodNumber) {
- if (value === '') {
- return 0;
- }
- return parseInt(value, 10);
- }
- return value;
- };
-
- const handleChange = (e: React.ChangeEvent) => {
- const newValue = convertValue(e.target.value, typeName);
- const validationResults = coercedSchema.safeParse(newValue);
-
- if (!validationResults.success) {
- setError(validationResults.error.issues[0].message);
- agentPolicyFormContext?.updateAdvancedSettingsHasErrors(true);
- } else {
- setError('');
- agentPolicyFormContext?.updateAdvancedSettingsHasErrors(false);
- }
-
- const newAdvancedSettings = {
- ...(agentPolicyFormContext?.agentPolicy.advanced_settings ?? {}),
- [settingsConfig.api_field.name]: newValue,
- };
-
- agentPolicyFormContext?.updateAgentPolicy({ advanced_settings: newAdvancedSettings });
- };
-
- const fieldValue =
- agentPolicyFormContext?.agentPolicy.advanced_settings?.[settingsConfig.api_field.name] ??
- defaultValue;
-
- return (
- {settingsConfig.title}}
- description={
- <>
- {settingsConfig.description}.{' '}
-
- Learn more.
-
- >
- }
- >
-
- {renderItem({ fieldValue, handleChange, isInvalid: !!error, fieldKey, coercedSchema })}
-
-
- );
-};
-
export function ConfiguredSettings({
configuredSettings,
}: {
@@ -137,21 +68,17 @@ export function ConfiguredSettings({
}) {
return (
<>
- {configuredSettings.map((configuredSetting) => {
- const Component = settingComponentRegistry.get(
- configuredSetting.schema instanceof z.ZodDefault
- ? configuredSetting.schema._def.innerType._def.typeName === 'ZodEffects'
- ? configuredSetting.schema._def.innerType._def.schema._def.typeName
- : configuredSetting.schema._def.innerType._def.typeName
- : configuredSetting.schema._def.typeName
- );
+ {configuredSettings
+ .filter((configuredSetting) => !configuredSetting.hidden)
+ .map((configuredSetting) => {
+ const Component = settingComponentRegistry.get(getInnerType(configuredSetting.schema));
- if (!Component) {
- throw new Error(`Unknown setting type: ${configuredSetting.schema._type}}`);
- }
+ if (!Component) {
+ throw new Error(`Unknown setting type: ${configuredSetting.schema._type}}`);
+ }
- return ;
- })}
+ return ;
+ })}
>
);
}
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_group.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_group.tsx
new file mode 100644
index 0000000000000..57d873d7a0560
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_group.tsx
@@ -0,0 +1,144 @@
+/*
+ * 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 { z, ZodFirstPartyTypeKind } from 'zod';
+import React, { useState } from 'react';
+import {
+ EuiFieldNumber,
+ EuiFieldText,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiFormRow,
+ EuiSwitch,
+} from '@elastic/eui';
+
+import type { SettingsConfig } from '../../../../../common/settings/types';
+import { useAgentPolicyFormContext } from '../../sections/agent_policy/components/agent_policy_form';
+
+import {
+ convertValue,
+ getInnerType,
+ SettingsFieldWrapper,
+ validateSchema,
+} from './settings_field_wrapper';
+
+export const SettingsFieldGroup: React.FC<{ settingsConfig: SettingsConfig }> = ({
+ settingsConfig,
+}) => {
+ const [errors, setErrors] = useState<{ [key: string]: string }>({});
+ const agentPolicyFormContext = useAgentPolicyFormContext();
+ const shape = settingsConfig.schema._def.innerType._def.shape();
+
+ return (
+ (
+
+ {Object.keys(shape).map((key) => {
+ const field = shape[key];
+ const fieldKey = `configuredSetting-${settingsConfig.name}-${key}`;
+ const defaultValue: number =
+ field instanceof z.ZodDefault ? field._def.defaultValue() : undefined;
+ const coercedSchema = field as z.ZodString;
+ const fieldValue =
+ agentPolicyFormContext?.agentPolicy.advanced_settings?.[
+ settingsConfig.api_field.name
+ ]?.[key] ?? defaultValue;
+ const type = getInnerType(field);
+
+ const description = field._def.description ?? key;
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const newValue = convertValue(e.target.value, type);
+ updateFieldValue(newValue);
+ };
+
+ const updateFieldValue = (newValue: any) => {
+ const validationError = validateSchema(coercedSchema, newValue);
+
+ if (validationError) {
+ setErrors({ ...errors, [key]: validationError });
+ agentPolicyFormContext?.updateAdvancedSettingsHasErrors(true);
+ } else {
+ setErrors({ ...errors, [key]: '' });
+ agentPolicyFormContext?.updateAdvancedSettingsHasErrors(false);
+ }
+
+ const newApiFieldValue = {
+ ...agentPolicyFormContext?.agentPolicy.advanced_settings?.[
+ settingsConfig.api_field.name
+ ],
+ [key]: newValue,
+ };
+
+ const newAdvancedSettings = {
+ ...(agentPolicyFormContext?.agentPolicy.advanced_settings ?? {}),
+ [settingsConfig.api_field.name]: newApiFieldValue,
+ };
+
+ agentPolicyFormContext?.updateAgentPolicy({ advanced_settings: newAdvancedSettings });
+ };
+
+ const getFormField = () => {
+ switch (type) {
+ case ZodFirstPartyTypeKind.ZodNumber:
+ return (
+
+ );
+ case ZodFirstPartyTypeKind.ZodString:
+ return (
+
+ );
+ case ZodFirstPartyTypeKind.ZodBoolean:
+ return (
+ {
+ updateFieldValue(e.target.checked);
+ }}
+ />
+ );
+ default:
+ return <>>;
+ }
+ };
+
+ return (
+
+
+ {getFormField()}
+
+
+ );
+ })}
+
+ )}
+ />
+ );
+};
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_wrapper.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_wrapper.tsx
new file mode 100644
index 0000000000000..a19c8e67c1080
--- /dev/null
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/settings_field_wrapper.tsx
@@ -0,0 +1,98 @@
+/*
+ * 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 { z, ZodFirstPartyTypeKind } from 'zod';
+import React, { useState } from 'react';
+import { EuiDescribedFormGroup, EuiFormRow, EuiLink } from '@elastic/eui';
+
+import type { SettingsConfig } from '../../../../../common/settings/types';
+import { useAgentPolicyFormContext } from '../../sections/agent_policy/components/agent_policy_form';
+
+export const convertValue = (value: string, type: keyof typeof ZodFirstPartyTypeKind): any => {
+ if (type === ZodFirstPartyTypeKind.ZodNumber) {
+ if (value === '') {
+ return 0;
+ }
+ return parseInt(value, 10);
+ }
+ return value;
+};
+
+export const validateSchema = (coercedSchema: z.ZodString, newValue: any): string | undefined => {
+ const validationResults = coercedSchema.safeParse(newValue);
+
+ if (!validationResults.success) {
+ return validationResults.error.issues[0].message;
+ }
+};
+
+export const SettingsFieldWrapper: React.FC<{
+ settingsConfig: SettingsConfig;
+ typeName: keyof typeof ZodFirstPartyTypeKind;
+ renderItem: Function;
+}> = ({ settingsConfig, typeName, renderItem }) => {
+ const [error, setError] = useState('');
+ const agentPolicyFormContext = useAgentPolicyFormContext();
+
+ const fieldKey = `configuredSetting-${settingsConfig.name}`;
+ const defaultValue: number =
+ settingsConfig.schema instanceof z.ZodDefault
+ ? settingsConfig.schema._def.defaultValue()
+ : undefined;
+ const coercedSchema = settingsConfig.schema as z.ZodString;
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const newValue = convertValue(e.target.value, typeName);
+ const validationError = validateSchema(coercedSchema, newValue);
+
+ if (validationError) {
+ setError(validationError);
+ agentPolicyFormContext?.updateAdvancedSettingsHasErrors(true);
+ } else {
+ setError('');
+ agentPolicyFormContext?.updateAdvancedSettingsHasErrors(false);
+ }
+
+ const newAdvancedSettings = {
+ ...(agentPolicyFormContext?.agentPolicy.advanced_settings ?? {}),
+ [settingsConfig.api_field.name]: newValue,
+ };
+
+ agentPolicyFormContext?.updateAgentPolicy({ advanced_settings: newAdvancedSettings });
+ };
+
+ const fieldValue =
+ agentPolicyFormContext?.agentPolicy.advanced_settings?.[settingsConfig.api_field.name] ??
+ defaultValue;
+
+ return (
+ {settingsConfig.title}}
+ description={
+ <>
+ {settingsConfig.description}.{' '}
+
+ Learn more.
+
+ >
+ }
+ >
+
+ {renderItem({ fieldValue, handleChange, isInvalid: !!error, fieldKey, coercedSchema })}
+
+
+ );
+};
+
+export const getInnerType = (schema: z.ZodType) => {
+ return schema instanceof z.ZodDefault
+ ? schema._def.innerType._def.typeName === 'ZodEffects'
+ ? schema._def.innerType._def.schema._def.typeName
+ : schema._def.innerType._def.typeName
+ : schema._def.typeName;
+};