Skip to content

Commit

Permalink
[8.x] [UII] Expose advanced file logging config in UI (#200274) (#201021
Browse files Browse the repository at this point in the history
)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[UII] Expose advanced file logging config in UI
(#200274)](#200274)

<!--- Backport version: 8.9.8 -->

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

<!--BACKPORT [{"author":{"name":"Jen
Huang","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-11-20T17:41:50Z","message":"[UII]
Expose advanced file logging config in UI (#200274)\n\n##
Summary\r\n\r\nResolves
[#192237](https://github.com/elastic/kibana/issues/192237).\r\nThis PR
exposes the following Elastic Agent file logging
configuration\r\noptions in the agent policy advanced settings
UI:\r\n\r\n```\r\nagent.logging.to_files\r\nagent.logging.files.rotateeverybytes
\r\nagent.logging.files.keepfiles\r\nagent.logging.files.interval\r\n```\r\n\r\n<img
width=\"1237\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8de9023c-29a0-4ecf-803a-d8c0c4b87616\">\r\n\r\nThis
PR also does some clean up on the default values for all
these\r\nconfigured advanced settings so that when user has not touched
them, the\r\ndefault values do not get written into the agent policy
saved object.\r\n[More
info\r\nhere](https://github.com/elastic/kibana/pull/200274#discussion_r1849142612).\r\n\r\nIt
also fixes adds missing response schemas for the advanced
settings.\r\n\r\n### Checklist\r\n\r\nCheck the PR satisfies following
conditions. \r\n\r\nReviewers should verify this PR satisfies this list
as well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>","sha":"0aa63a7eccea009174db782c57577226ea252bff","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Fleet","v9.0.0","release_note:feature","backport:prev-minor"],"number":200274,"url":"https://github.com/elastic/kibana/pull/200274","mergeCommit":{"message":"[UII]
Expose advanced file logging config in UI (#200274)\n\n##
Summary\r\n\r\nResolves
[#192237](https://github.com/elastic/kibana/issues/192237).\r\nThis PR
exposes the following Elastic Agent file logging
configuration\r\noptions in the agent policy advanced settings
UI:\r\n\r\n```\r\nagent.logging.to_files\r\nagent.logging.files.rotateeverybytes
\r\nagent.logging.files.keepfiles\r\nagent.logging.files.interval\r\n```\r\n\r\n<img
width=\"1237\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8de9023c-29a0-4ecf-803a-d8c0c4b87616\">\r\n\r\nThis
PR also does some clean up on the default values for all
these\r\nconfigured advanced settings so that when user has not touched
them, the\r\ndefault values do not get written into the agent policy
saved object.\r\n[More
info\r\nhere](https://github.com/elastic/kibana/pull/200274#discussion_r1849142612).\r\n\r\nIt
also fixes adds missing response schemas for the advanced
settings.\r\n\r\n### Checklist\r\n\r\nCheck the PR satisfies following
conditions. \r\n\r\nReviewers should verify this PR satisfies this list
as well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>","sha":"0aa63a7eccea009174db782c57577226ea252bff"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/200274","number":200274,"mergeCommit":{"message":"[UII]
Expose advanced file logging config in UI (#200274)\n\n##
Summary\r\n\r\nResolves
[#192237](https://github.com/elastic/kibana/issues/192237).\r\nThis PR
exposes the following Elastic Agent file logging
configuration\r\noptions in the agent policy advanced settings
UI:\r\n\r\n```\r\nagent.logging.to_files\r\nagent.logging.files.rotateeverybytes
\r\nagent.logging.files.keepfiles\r\nagent.logging.files.interval\r\n```\r\n\r\n<img
width=\"1237\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/8de9023c-29a0-4ecf-803a-d8c0c4b87616\">\r\n\r\nThis
PR also does some clean up on the default values for all
these\r\nconfigured advanced settings so that when user has not touched
them, the\r\ndefault values do not get written into the agent policy
saved object.\r\n[More
info\r\nhere](https://github.com/elastic/kibana/pull/200274#discussion_r1849142612).\r\n\r\nIt
also fixes adds missing response schemas for the advanced
settings.\r\n\r\n### Checklist\r\n\r\nCheck the PR satisfies following
conditions. \r\n\r\nReviewers should verify this PR satisfies this list
as well.\r\n\r\n- [x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common
scenarios\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<[email protected]>","sha":"0aa63a7eccea009174db782c57577226ea252bff"}}]}]
BACKPORT-->
  • Loading branch information
jen-huang authored Nov 20, 2024
1 parent 058ea38 commit fd6dc18
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 50 deletions.
78 changes: 75 additions & 3 deletions x-pack/plugins/fleet/common/settings/agent_policy_settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
api_field: {
name: 'agent_limits_go_max_procs',
},
schema: z.number().int().min(0).default(0),
schema: z.number().int().min(0),
},
{
name: 'agent.download.timeout',
Expand All @@ -59,7 +59,7 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
api_field: {
name: 'agent_download_timeout',
},
schema: zodStringWithDurationValidation.default('2h'),
schema: zodStringWithDurationValidation,
},
{
name: 'agent.download.target_directory',
Expand Down Expand Up @@ -103,7 +103,7 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
),
learnMoreLink:
'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings',
schema: zodStringWithDurationValidation.default('30s'),
schema: zodStringWithDurationValidation,
},
{
name: 'agent.logging.level',
Expand All @@ -124,4 +124,76 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
'https://www.elastic.co/guide/en/fleet/current/agent-policy.html#agent-policy-log-level',
schema: z.enum(AGENT_LOG_LEVELS).default(DEFAULT_LOG_LEVEL),
},
{
name: 'agent.logging.to_files',
title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingToFilesTitle', {
defaultMessage: 'Agent logging to files',
}),
description: (
<FormattedMessage
id="xpack.fleet.settings.agentPolicyAdvanced.agentLoggingToFilesDescription"
defaultMessage="Enables logging to rotating files."
/>
),
api_field: {
name: 'agent_logging_to_files',
},
learnMoreLink:
'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings',
schema: z.boolean().default(true),
},
{
name: 'agent.logging.files.rotateeverybytes',
title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileSizeTitle', {
defaultMessage: 'Agent logging file size limit',
}),
description: (
<FormattedMessage
id="xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileSizeDescription"
defaultMessage="Configure log file size limit in bytes. If limit is reached, log file will be automatically rotated."
/>
),
api_field: {
name: 'agent_logging_files_rotateeverybytes',
},
learnMoreLink:
'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings',
schema: z.number().int().min(0),
},
{
name: 'agent.logging.files.keepfiles',
title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileLimitTitle', {
defaultMessage: 'Agent logging number of files',
}),
description: (
<FormattedMessage
id="xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileLimitDescription"
defaultMessage="Number of rotated log files to keep. Oldest files will be deleted first."
/>
),
api_field: {
name: 'agent_logging_files_keepfiles',
},
learnMoreLink:
'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings',
schema: z.number().int().min(0),
},
{
name: 'agent.logging.files.interval',
title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileIntervalitle', {
defaultMessage: 'Agent logging number of files',
}),
description: (
<FormattedMessage
id="xpack.fleet.settings.agentPolicyAdvanced.agentLoggingFileIntervalescription"
defaultMessage="Enable log file rotation on time intervals in addition to size-based rotation, i.e. 24h, 7d."
/>
),
api_field: {
name: 'agent_logging_files_interval',
},
learnMoreLink:
'https://www.elastic.co/guide/en/fleet/current/elastic-agent-standalone-logging-config.html#elastic-agent-standalone-logging-settings',
schema: zodStringWithDurationValidation,
},
];
12 changes: 12 additions & 0 deletions x-pack/plugins/fleet/common/types/models/agent_policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ export interface FullAgentPolicy {
uninstall_token_hash: string;
signing_key: string;
};
logging?: {
level?: string;
to_files?: boolean;
files?: {
rotateeverybytes?: number;
keepfiles?: number;
interval?: string;
};
};
limits?: {
go_max_procs?: number;
};
};
secret_references?: PolicySecretReference[];
signed?: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,35 @@ describe('ConfiguredSettings', () => {
expect(mockUpdateAdvancedSettingsHasErrors).toHaveBeenCalledWith(true);
});

it('should render boolean field using checkbox', () => {
const result = render([
{
name: 'agent.logging.to_files',
title: 'Agent logging to files',
description: 'Description',
learnMoreLink: '',
api_field: {
name: 'agent_logging_to_files',
},
schema: z.boolean().default(false),
},
]);

expect(result.getByText('Agent logging to files')).not.toBeNull();
const input = result.getByTestId('configuredSetting-agent.logging.to_files');
expect(input).not.toBeChecked();

act(() => {
fireEvent.click(input);
});

expect(mockUpdateAgentPolicy).toHaveBeenCalledWith(
expect.objectContaining({
advanced_settings: expect.objectContaining({ agent_logging_to_files: true }),
})
);
});

it('should not render field if hidden', () => {
const result = render([
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

import { ZodFirstPartyTypeKind } from '@kbn/zod';
import React from 'react';
import { EuiFieldNumber, EuiFieldText, EuiSelect } from '@elastic/eui';
import { EuiCheckbox, EuiFieldNumber, EuiFieldText, EuiSelect } from '@elastic/eui';

import { i18n } from '@kbn/i18n';

import type { SettingsConfig } from '../../../../../common/settings/types';

Expand Down Expand Up @@ -68,7 +70,7 @@ settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodEnum, ({ disabled, ...sett
<SettingsFieldWrapper
disabled={disabled}
settingsConfig={settingsConfig}
typeName={ZodFirstPartyTypeKind.ZodString}
typeName={ZodFirstPartyTypeKind.ZodEnum}
renderItem={({ fieldKey, fieldValue, handleChange }: any) => (
<EuiSelect
data-test-subj={fieldKey}
Expand All @@ -86,6 +88,30 @@ settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodEnum, ({ disabled, ...sett
);
});

settingComponentRegistry.set(
ZodFirstPartyTypeKind.ZodBoolean,
({ disabled, ...settingsConfig }) => {
return (
<SettingsFieldWrapper
disabled={disabled}
settingsConfig={settingsConfig}
typeName={ZodFirstPartyTypeKind.ZodBoolean}
renderItem={({ fieldKey, fieldValue, handleChange }: any) => (
<EuiCheckbox
data-test-subj={fieldKey}
id={fieldKey}
label={i18n.translate('xpack.fleet.configuredSettings.genericCheckboxLabel', {
defaultMessage: 'Enable',
})}
checked={fieldValue}
onChange={handleChange}
/>
)}
/>
);
}
);

export function ConfiguredSettings({
configuredSettings,
disabled,
Expand All @@ -101,7 +127,7 @@ export function ConfiguredSettings({
const Component = settingComponentRegistry.get(getInnerType(configuredSetting.schema));

if (!Component) {
throw new Error(`Unknown setting type: ${configuredSetting.schema._type}}`);
throw new Error(`Unknown setting type: ${configuredSetting.schema._type}`);
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ 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 => {
export const convertValue = (
value: string | boolean,
type: keyof typeof ZodFirstPartyTypeKind
): any => {
if (type === ZodFirstPartyTypeKind.ZodNumber) {
if (value === '') {
return 0;
}
return parseInt(value, 10);
return parseInt(value as string, 10);
}
return value;
};
Expand Down Expand Up @@ -48,7 +51,8 @@ export const SettingsFieldWrapper: React.FC<{
const coercedSchema = settingsConfig.schema as z.ZodString;

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = convertValue(e.target.value, typeName);
const value = typeName === ZodFirstPartyTypeKind.ZodBoolean ? e.target.checked : e.target.value;
const newValue = convertValue(value, typeName);
const validationError = validateSchema(coercedSchema, newValue);

if (validationError) {
Expand Down Expand Up @@ -97,9 +101,13 @@ export const SettingsFieldWrapper: React.FC<{
};

export const getInnerType = (schema: z.ZodType<any, any>) => {
return schema instanceof z.ZodDefault
? schema._def.innerType._def.typeName === 'ZodEffects'
if (schema._def.innerType) {
return schema._def.innerType._def.typeName === 'ZodEffects'
? schema._def.innerType._def.schema._def.typeName
: schema._def.innerType._def.typeName
: schema._def.typeName;
: schema._def.innerType._def.typeName;
}
if (schema._def.typeName === 'ZodEffects') {
return schema._def.schema._def.typeName;
}
return schema._def.typeName;
};
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,10 @@ describe('getFullAgentPolicy', () => {
advanced_settings: {
agent_limits_go_max_procs: 2,
agent_logging_level: 'debug',
agent_logging_to_files: true,
agent_logging_files_rotateeverybytes: 10000,
agent_logging_files_keepfiles: 10,
agent_logging_files_interval: '7h',
},
});
const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy');
Expand All @@ -896,7 +900,11 @@ describe('getFullAgentPolicy', () => {
id: 'agent-policy',
agent: {
limits: { go_max_procs: 2 },
logging: { level: 'debug' },
logging: {
level: 'debug',
to_files: true,
files: { rotateeverybytes: 10000, keepfiles: 10, interval: '7h' },
},
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ describe('form_settings', () => {
).not.toThrow();
});

it('generate a valid API schema for api_field with default value', () => {
it('generate a valid API schema for api_field with default value but not add the value', () => {
const apiSchema = schema.object(_getSettingsAPISchema(TEST_SETTINGS));
const res = apiSchema.validate({ advanced_settings: {} });
expect(res).toEqual({ advanced_settings: { test_foo_default_value: 'test' } });
expect(res).toEqual({ advanced_settings: {} });
});
});

Expand Down
48 changes: 14 additions & 34 deletions x-pack/plugins/fleet/server/services/form_settings/form_settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,19 @@ export function _getSettingsAPISchema(settings: SettingsConfig[]): Props {
if (!setting.api_field) {
return;
}
const defaultValueRes = setting.schema.safeParse(undefined);
const defaultValue = defaultValueRes.success ? defaultValueRes.data : undefined;
if (defaultValue) {
validations[setting.api_field.name] = schema.oneOf(
[
schema.any({
validate: (val: any) => {
const res = setting.schema.safeParse(val);
if (!res.success) {
return stringifyZodError(res.error);
}
},
}),
schema.literal(null),
],
{
defaultValue,
}
);
} else {
validations[setting.api_field.name] = schema.maybe(
schema.nullable(
schema.any({
validate: (val: any) => {
const res = setting.schema.safeParse(val);
if (!res.success) {
return stringifyZodError(res.error);
}
},
})
)
);
}
validations[setting.api_field.name] = schema.maybe(
schema.oneOf([
schema.literal(null),
schema.any({
validate: (val: any) => {
const res = setting.schema.safeParse(val);
if (!res.success) {
return stringifyZodError(res.error);
}
},
}),
])
);
});

const advancedSettingsValidations: Props = {
Expand Down Expand Up @@ -89,7 +69,7 @@ export function _getSettingsValuesForAgentPolicy(
}

const val = agentPolicy.advanced_settings?.[setting.api_field.name];
if (val) {
if (val !== undefined) {
settingsValues[setting.name] = val;
}
});
Expand Down

0 comments on commit fd6dc18

Please sign in to comment.