Skip to content

Commit

Permalink
[Index Management] Support managed_by field and fix never delete data…
Browse files Browse the repository at this point in the history
… logic (elastic#169064)
  • Loading branch information
sabarasaba authored Oct 31, 2023
1 parent 7ad80a0 commit 40e524b
Show file tree
Hide file tree
Showing 16 changed files with 548 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ export type TestSubjects =
| 'filter-option-h'
| 'infiniteRetentionPeriod.input'
| 'saveButton'
| 'dsIsFullyManagedByILM'
| 'someIndicesAreManagedByILMCallout'
| 'viewIlmPolicyLink'
| 'viewAllIndicesLink'
| 'dataRetentionEnabledField.input'
| 'enrichPoliciesInsuficientPrivileges'
| 'dataRetentionDetail'
| 'createIndexSaveButton';
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,12 @@ export const createDataStreamPayload = (dataStream: Partial<DataStream>): DataSt
{
name: 'indexName',
uuid: 'indexId',
preferILM: false,
managedBy: 'Data stream lifecycle',
},
],
generation: 1,
nextGenerationManagedBy: 'Data stream lifecycle',
health: 'green',
indexTemplateName: 'indexTemplate',
storageSize: '1b',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,32 @@ describe('Data Streams tab', () => {
);
});

test('can disable lifecycle', async () => {
const {
actions: { clickNameAt, clickEditDataRetentionButton },
} = testBed;

await clickNameAt(0);

clickEditDataRetentionButton();

httpRequestsMockHelpers.setEditDataRetentionResponse('dataStream1', {
success: true,
});

testBed.form.toggleEuiSwitch('dataRetentionEnabledField.input');

await act(async () => {
testBed.find('saveButton').simulate('click');
});
testBed.component.update();

expect(httpSetup.put).toHaveBeenLastCalledWith(
`${API_BASE_PATH}/data_streams/dataStream1/data_retention`,
expect.objectContaining({ body: JSON.stringify({ enabled: false }) })
);
});

test('allows to set infinite retention period', async () => {
const {
actions: { clickNameAt, clickEditDataRetentionButton },
Expand Down Expand Up @@ -499,6 +525,110 @@ describe('Data Streams tab', () => {
expect(findDetailPanelDataRetentionDetail().exists()).toBeTruthy();
});
});

describe('shows all possible states according to who manages the data stream', () => {
const ds1 = createDataStreamPayload({
name: 'dataStream1',
nextGenerationManagedBy: 'Index Lifecycle Management',
lifecycle: undefined,
indices: [
{
managedBy: 'Index Lifecycle Management',
name: 'indexName',
uuid: 'indexId',
preferILM: true,
},
],
});

const ds2 = createDataStreamPayload({
name: 'dataStream2',
nextGenerationManagedBy: 'Data stream lifecycle',
lifecycle: {
enabled: true,
data_retention: '7d',
},
indices: [
{
managedBy: 'Index Lifecycle Management',
name: 'indexName1',
uuid: 'indexId1',
preferILM: true,
},
{
managedBy: 'Index Lifecycle Management',
name: 'indexName2',
uuid: 'indexId2',
preferILM: true,
},
{
managedBy: 'Index Lifecycle Management',
name: 'indexName3',
uuid: 'indexId3',
preferILM: true,
},
{
managedBy: 'Index Lifecycle Management',
name: 'indexName4',
uuid: 'indexId4',
preferILM: true,
},
],
});

beforeEach(async () => {
const { setLoadDataStreamsResponse } = httpRequestsMockHelpers;

setLoadDataStreamsResponse([ds1, ds2]);

testBed = await setup(httpSetup, {
history: createMemoryHistory(),
url: urlServiceMock,
});

await act(async () => {
testBed.actions.goToDataStreamsList();
});
testBed.component.update();
});

test('when fully managed by ILM, user cannot edit data retention', async () => {
const { setLoadDataStreamResponse } = httpRequestsMockHelpers;

setLoadDataStreamResponse(ds1.name, ds1);

const { actions, find, exists } = testBed;

await actions.clickNameAt(0);
expect(find('dataRetentionDetail').text()).toBe('Disabled');

// There should be a warning that the data stream is fully managed by ILM
expect(exists('dsIsFullyManagedByILM')).toBe(true);

// Edit data retention button should not be visible
testBed.find('manageDataStreamButton').simulate('click');
expect(exists('editDataRetentionButton')).toBe(false);
});

test('when partially managed by dsl but has backing indices managed by ILM should show a warning', async () => {
const { setLoadDataStreamResponse } = httpRequestsMockHelpers;

setLoadDataStreamResponse(ds2.name, ds2);

const { actions, find, exists } = testBed;

await actions.clickNameAt(1);
expect(find('dataRetentionDetail').text()).toBe('7d');

actions.clickEditDataRetentionButton();

// There should be a warning that the data stream is managed by DSL
// but the backing indices that are managed by ILM wont be affected.
expect(exists('someIndicesAreManagedByILMCallout')).toBe(true);
expect(exists('viewIlmPolicyLink')).toBe(true);
expect(exists('viewAllIndicesLink')).toBe(true);
});
});
});

describe('when there are special characters', () => {
Expand Down Expand Up @@ -569,33 +699,6 @@ describe('Data Streams tab', () => {
expect(findDetailPanelIlmPolicyLink().prop('href')).toBe('/test/my_ilm_policy');
});

test('with ILM updating data retention should be disabled', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

const dataStreamForDetailPanel = createDataStreamPayload({
name: 'dataStream1',
ilmPolicyName: 'my_ilm_policy',
});

setLoadDataStreamsResponse([dataStreamForDetailPanel]);
setLoadDataStreamResponse(dataStreamForDetailPanel.name, dataStreamForDetailPanel);

testBed = await setup(httpSetup, {
history: createMemoryHistory(),
url: urlServiceMock,
});
await act(async () => {
testBed.actions.goToDataStreamsList();
});
testBed.component.update();

const { actions } = testBed;
await actions.clickNameAt(0);

testBed.find('manageDataStreamButton').simulate('click');
expect(testBed.find('editDataRetentionButton').exists()).toBeFalsy();
});

test('with an ILM url locator and no ILM policy', async () => {
const { setLoadDataStreamsResponse, setLoadDataStreamResponse } = httpRequestsMockHelpers;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,28 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs
privileges,
hidden,
lifecycle,
next_generation_managed_by: nextGenerationManagedBy,
} = dataStreamFromEs;

return {
name,
timeStampField,
indices: indices.map(
// eslint-disable-next-line @typescript-eslint/naming-convention
({ index_name, index_uuid }: { index_name: string; index_uuid: string }) => ({
name: index_name,
uuid: index_uuid,
({
index_name: indexName,
index_uuid: indexUuid,
prefer_ilm: preferILM,
managed_by: managedBy,
}: {
index_name: string;
index_uuid: string;
prefer_ilm: boolean;
managed_by: string;
}) => ({
name: indexName,
uuid: indexUuid,
preferILM,
managedBy,
})
),
generation,
Expand All @@ -46,6 +58,7 @@ export function deserializeDataStream(dataStreamFromEs: EnhancedDataStreamFromEs
privileges,
hidden,
lifecycle,
nextGenerationManagedBy,
};
}

Expand Down
21 changes: 14 additions & 7 deletions x-pack/plugins/index_management/common/types/data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,27 @@ type Privileges = PrivilegesFromEs;

export type HealthFromEs = 'GREEN' | 'YELLOW' | 'RED';

export interface DataStreamIndexFromEs {
index_name: string;
index_uuid: string;
prefer_ilm: boolean;
managed_by: string;
}

export type Health = 'green' | 'yellow' | 'red';

export interface EnhancedDataStreamFromEs extends IndicesDataStream {
store_size?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size'];
store_size_bytes?: IndicesDataStreamsStatsDataStreamsStatsItem['store_size_bytes'];
maximum_timestamp?: IndicesDataStreamsStatsDataStreamsStatsItem['maximum_timestamp'];
indices: DataStreamIndexFromEs[];
next_generation_managed_by: string;
privileges: {
delete_index: boolean;
manage_data_stream_lifecycle: boolean;
};
}

export interface DataStreamIndexFromEs {
index_name: string;
index_uuid: string;
}

export type Health = 'green' | 'yellow' | 'red';

export interface DataStream {
name: string;
timeStampField: TimestampField;
Expand All @@ -59,6 +63,7 @@ export interface DataStream {
_meta?: Metadata;
privileges: Privileges;
hidden: boolean;
nextGenerationManagedBy: string;
lifecycle?: IndicesDataLifecycleWithRollover & {
enabled?: boolean;
};
Expand All @@ -67,4 +72,6 @@ export interface DataStream {
export interface DataStreamIndex {
name: string;
uuid: string;
preferILM: boolean;
managedBy: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,42 @@ export const getLifecycleValue = (

return lifecycle?.data_retention;
};

export const isDataStreamFullyManagedByILM = (dataStream?: DataStream | null) => {
return (
dataStream?.nextGenerationManagedBy?.toLowerCase() === 'index lifecycle management' &&
dataStream?.indices?.every(
(index) => index.managedBy.toLowerCase() === 'index lifecycle management'
)
);
};

export const isDataStreamFullyManagedByDSL = (dataStream?: DataStream | null) => {
return (
dataStream?.nextGenerationManagedBy?.toLowerCase() === 'data stream lifecycle' &&
dataStream?.indices?.every((index) => index.managedBy.toLowerCase() === 'data stream lifecycle')
);
};

export const isDSLWithILMIndices = (dataStream?: DataStream | null) => {
if (dataStream?.nextGenerationManagedBy?.toLowerCase() === 'data stream lifecycle') {
const ilmIndices = dataStream?.indices?.filter(
(index) => index.managedBy.toLowerCase() === 'index lifecycle management'
);
const dslIndices = dataStream?.indices?.filter(
(index) => index.managedBy.toLowerCase() === 'data stream lifecycle'
);

// When there arent any ILM indices, there's no need to show anything.
if (!ilmIndices?.length) {
return;
}

return {
ilmIndices,
dslIndices,
};
}

return;
};
Loading

0 comments on commit 40e524b

Please sign in to comment.