Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Index Management] Fix globalMaxRetention display error in data retention modal (#198113) #198534

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { getLifecycleValue } from './data_streams';
import { deserializeGlobalMaxRetention, getLifecycleValue } from './data_streams';

describe('Data stream helpers', () => {
describe('getLifecycleValue', () => {
Expand Down Expand Up @@ -45,4 +45,36 @@ describe('Data stream helpers', () => {
).toBe('5 days');
});
});

describe('deserializeGlobalMaxRetention', () => {
it('if globalMaxRetention is undefined', () => {
expect(deserializeGlobalMaxRetention(undefined)).toEqual({});
});

it('split globalMaxRetention size and units', () => {
expect(deserializeGlobalMaxRetention('1000h')).toEqual({
size: '1000',
unit: 'h',
unitText: 'hours',
});
});

it('support all of the units that are accepted by es', () => {
expect(deserializeGlobalMaxRetention('1000ms')).toEqual({
size: '1000',
unit: 'ms',
unitText: 'milliseconds',
});
expect(deserializeGlobalMaxRetention('1000micros')).toEqual({
size: '1000',
unit: 'micros',
unitText: 'microseconds',
});
expect(deserializeGlobalMaxRetention('1000nanos')).toEqual({
size: '1000',
unit: 'nanos',
unitText: 'nanoseconds',
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,19 @@ export const isDSLWithILMIndices = (dataStream?: DataStream | null) => {

return;
};

export const deserializeGlobalMaxRetention = (globalMaxRetention?: string) => {
if (!globalMaxRetention) {
return {};
}

const { size, unit } = splitSizeAndUnits(globalMaxRetention);
const availableTimeUnits = [...timeUnits, ...extraTimeUnits];
const match = availableTimeUnits.find((timeUnit) => timeUnit.value === unit);

return {
size,
unit,
unitText: match?.text ?? unit,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import { getIndexListUri } from '../../../../services/routing';
import { documentationService } from '../../../../services/documentation';
import { splitSizeAndUnits, DataStream } from '../../../../../../common';
import { timeUnits } from '../../../../constants/time_units';
import { isDSLWithILMIndices } from '../../../../lib/data_streams';
import { deserializeGlobalMaxRetention, isDSLWithILMIndices } from '../../../../lib/data_streams';
import { useAppContext } from '../../../../app_context';
import { UnitField } from '../../../../components/shared';
import { updateDataRetention } from '../../../../services/api';
Expand Down Expand Up @@ -214,6 +214,7 @@ export const EditDataRetentionModal: React.FunctionComponent<Props> = ({
const { history } = useAppContext();
const dslWithIlmIndices = isDSLWithILMIndices(dataStream);
const { size, unit } = splitSizeAndUnits(lifecycle?.data_retention as string);
const globalMaxRetention = deserializeGlobalMaxRetention(lifecycle?.globalMaxRetention);
const {
services: { notificationService },
config: { enableTogglingDataRetention, enableProjectLevelRetentionChecks },
Expand Down Expand Up @@ -331,8 +332,11 @@ export const EditDataRetentionModal: React.FunctionComponent<Props> = ({
<>
<FormattedMessage
id="xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.modalTitleText"
defaultMessage="Maximum data retention period is {maxRetention} days"
values={{ maxRetention: lifecycle?.globalMaxRetention.slice(0, -1) }}
defaultMessage="Maximum data retention period is {maxRetention} {unitText}"
values={{
maxRetention: globalMaxRetention.size,
unitText: globalMaxRetention.unitText,
}}
/>
<EuiSpacer />
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ describe('isBiggerThanGlobalMaxRetention', () => {
});
});

it('should correctly compare retention in all of the units that are accepted by es', () => {
// 1000 milliseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(1, 's', '1000ms')).toBeUndefined();
expect(isBiggerThanGlobalMaxRetention(2, 's', '1000ms')).toEqual({
message: 'Maximum data retention period on this project is 1000 milliseconds.',
});

// 1000000 microseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(1, 's', '1000000micros')).toBeUndefined();
expect(isBiggerThanGlobalMaxRetention(2, 'm', '1000000micros')).toEqual({
message: 'Maximum data retention period on this project is 1000000 microseconds.',
});

// 1000000000 microseconds = 1 seconds
expect(isBiggerThanGlobalMaxRetention(2, 's', '1000000000nanos'));
expect(isBiggerThanGlobalMaxRetention(2, 'h', '1000000000nanos')).toEqual({
message: 'Maximum data retention period on this project is 1000000000 nanoseconds.',
});
});

it('should throw an error for unknown time units', () => {
expect(() => isBiggerThanGlobalMaxRetention(10, 'x', '30d')).toThrow('Unknown unit: x');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,44 @@

import { i18n } from '@kbn/i18n';
import { splitSizeAndUnits } from '../../../../../../common';
import { deserializeGlobalMaxRetention } from '../../../../lib/data_streams';

const convertToMinutes = (value: string) => {
const convertToSeconds = (value: string) => {
const { size, unit } = splitSizeAndUnits(value);
const sizeNum = parseInt(size, 10);

switch (unit) {
case 'd':
// days to minutes
return sizeNum * 24 * 60;
// days to seconds
return sizeNum * 24 * 60 * 60;
case 'h':
// hours to minutes
return sizeNum * 60;
// hours to seconds
return sizeNum * 60 * 60;
case 'm':
// minutes to minutes
return sizeNum;
// minutes to seconds
return sizeNum * 60;
case 's':
// seconds to minutes (round up if any remainder)
return Math.ceil(sizeNum / 60);
// seconds to seconds
return sizeNum;
case 'ms':
// milliseconds to seconds
return sizeNum / 1000;
case 'micros':
// microseconds to seconds
return sizeNum / 1000 / 1000;
case 'nanos':
// nanoseconds to seconds
return sizeNum / 1000 / 1000 / 1000;
default:
throw new Error(`Unknown unit: ${unit}`);
}
};

const isRetentionBiggerThan = (valueA: string, valueB: string) => {
const minutesA = convertToMinutes(valueA);
const minutesB = convertToMinutes(valueB);
const secondsA = convertToSeconds(valueA);
const secondsB = convertToSeconds(valueB);

return minutesA > minutesB;
return secondsA > secondsB;
};

export const isBiggerThanGlobalMaxRetention = (
Expand All @@ -46,14 +56,19 @@ export const isBiggerThanGlobalMaxRetention = (
return undefined;
}

const { size, unitText } = deserializeGlobalMaxRetention(globalMaxRetention);
return isRetentionBiggerThan(`${retentionValue}${retentionTimeUnit}`, globalMaxRetention)
? {
message: i18n.translate(
'xpack.idxMgmt.dataStreamsDetailsPanel.editDataRetentionModal.dataRetentionFieldMaxError',
{
defaultMessage: 'Maximum data retention period on this project is {maxRetention} days.',
defaultMessage:
'Maximum data retention period on this project is {maxRetention} {unitText}.',
// Remove the unit from the globalMaxRetention value
values: { maxRetention: globalMaxRetention.slice(0, -1) },
values: {
maxRetention: size,
unitText,
},
}
),
}
Expand Down