Skip to content

Commit

Permalink
[ILM] Add "wait for snapshot" policy field to Delete phase (elastic#6…
Browse files Browse the repository at this point in the history
…8505)

* [ILM] Add "wait for snapshot" text field to edit policy form (Delete phase)

* [ILM] Add jest client integration tests for delete phase

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
yuliacech and elasticmachine committed Jun 11, 2020
1 parent 6c1f977 commit ce12f5c
Show file tree
Hide file tree
Showing 11 changed files with 337 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export const POLICY_NAME = 'my_policy';

export const DELETE_PHASE_POLICY = {
version: 1,
modified_date: Date.now(),
policy: {
phases: {
hot: {
min_age: '0ms',
actions: {
rollover: {
max_size: '50gb',
},
},
},
delete: {
min_age: '0ms',
actions: {
wait_for_snapshot: {
policy: 'my_snapshot_policy',
},
delete: {
delete_searchable_snapshot: true,
},
},
},
},
},
name: POLICY_NAME,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { act } from 'react-dom/test-utils';

import { registerTestBed, TestBed, TestBedConfig } from '../../../../../test_utils';

import { POLICY_NAME } from './contants';
import { TestSubjects } from '../helpers';

import { EditPolicy } from '../../../public/application/sections/edit_policy';
import { indexLifecycleManagementStore } from '../../../public/application/store';

const testBedConfig: TestBedConfig = {
store: () => indexLifecycleManagementStore(),
memoryRouter: {
initialEntries: [`/policies/edit/${POLICY_NAME}`],
componentRoutePath: `/policies/edit/:policyName`,
},
};

const initTestBed = registerTestBed(EditPolicy, testBedConfig);

export interface EditPolicyTestBed extends TestBed<TestSubjects> {
actions: {
setWaitForSnapshotPolicy: (snapshotPolicyName: string) => void;
savePolicy: () => void;
};
}

export const setup = async (): Promise<EditPolicyTestBed> => {
const testBed = await initTestBed();

const setWaitForSnapshotPolicy = (snapshotPolicyName: string) => {
const { component, form } = testBed;
form.setInputValue('waitForSnapshotField', snapshotPolicyName, true);
component.update();
};

const savePolicy = async () => {
const { component, find } = testBed;
await act(async () => {
find('savePolicyButton').simulate('click');
});
component.update();
};

return {
...testBed,
actions: {
setWaitForSnapshotPolicy,
savePolicy,
},
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { act } from 'react-dom/test-utils';

import { setupEnvironment } from '../helpers/setup_environment';

import { EditPolicyTestBed, setup } from './edit_policy.helpers';
import { DELETE_PHASE_POLICY } from './contants';

import { API_BASE_PATH } from '../../../common/constants';

window.scrollTo = jest.fn();

describe('<EditPolicy />', () => {
let testBed: EditPolicyTestBed;
const { server, httpRequestsMockHelpers } = setupEnvironment();
afterAll(() => {
server.restore();
});

describe('delete phase', () => {
beforeEach(async () => {
httpRequestsMockHelpers.setLoadPolicies([DELETE_PHASE_POLICY]);

await act(async () => {
testBed = await setup();
});

const { component } = testBed;
component.update();
});

test('wait for snapshot policy field should correctly display snapshot policy name', () => {
expect(testBed.find('waitForSnapshotField').props().value).toEqual(
DELETE_PHASE_POLICY.policy.phases.delete.actions.wait_for_snapshot.policy
);
});

test('wait for snapshot field should correctly update snapshot policy name', async () => {
const { actions } = testBed;

const newPolicyName = 'my_new_snapshot_policy';
actions.setWaitForSnapshotPolicy(newPolicyName);
await actions.savePolicy();

const expected = {
name: DELETE_PHASE_POLICY.name,
phases: {
...DELETE_PHASE_POLICY.policy.phases,
delete: {
...DELETE_PHASE_POLICY.policy.phases.delete,
actions: {
...DELETE_PHASE_POLICY.policy.phases.delete.actions,
wait_for_snapshot: {
policy: newPolicyName,
},
},
},
},
};

const latestRequest = server.requests[server.requests.length - 1];
expect(latestRequest.url).toBe(`${API_BASE_PATH}/policies`);
expect(latestRequest.method).toBe('POST');
expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected);
});

test('wait for snapshot field should delete action if field is empty', async () => {
const { actions } = testBed;

actions.setWaitForSnapshotPolicy('');
await actions.savePolicy();

const expected = {
name: DELETE_PHASE_POLICY.name,
phases: {
...DELETE_PHASE_POLICY.policy.phases,
delete: {
...DELETE_PHASE_POLICY.policy.phases.delete,
actions: {
...DELETE_PHASE_POLICY.policy.phases.delete.actions,
},
},
},
};
delete expected.phases.delete.actions.wait_for_snapshot;

const latestRequest = server.requests[server.requests.length - 1];
expect(JSON.parse(JSON.parse(latestRequest.requestBody).body)).toEqual(expected);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { SinonFakeServer, fakeServer } from 'sinon';
import { API_BASE_PATH } from '../../../common/constants';

export const init = () => {
const server = fakeServer.create();
server.respondImmediately = true;
server.respondWith([200, {}, 'DefaultServerResponse']);

return {
server,
httpRequestsMockHelpers: registerHttpRequestMockHelpers(server),
};
};

const registerHttpRequestMockHelpers = (server: SinonFakeServer) => {
const setLoadPolicies = (response: any = []) => {
server.respondWith('GET', `${API_BASE_PATH}/policies`, [
200,
{ 'Content-Type': 'application/json' },
JSON.stringify(response),
]);
};

return {
setLoadPolicies,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export type TestSubjects = 'waitForSnapshotField' | 'savePolicyButton';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import axios from 'axios';
import axiosXhrAdapter from 'axios/lib/adapters/xhr';

import { init as initHttp } from '../../../public/application/services/http';
import { init as initHttpRequests } from './http_requests';
import { init as initUiMetric } from '../../../public/application/services/ui_metric';
import { init as initNotification } from '../../../public/application/services/notification';

import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/public/mocks';

import {
notificationServiceMock,
fatalErrorsServiceMock,
} from '../../../../../../src/core/public/mocks';

const mockHttpClient = axios.create({ adapter: axiosXhrAdapter });

export const setupEnvironment = () => {
initUiMetric(usageCollectionPluginMock.createSetupContract());
initNotification(
notificationServiceMock.createSetupContract().toasts,
fatalErrorsServiceMock.createSetupContract()
);

mockHttpClient.interceptors.response.use(({ data }) => data);
initHttp(mockHttpClient);
const { server, httpRequestsMockHelpers } = initHttpRequests();

return {
server,
httpRequestsMockHelpers,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export const PHASE_PRIMARY_SHARD_COUNT: string = 'selectedPrimaryShardCount';
export const PHASE_REPLICA_COUNT: string = 'selectedReplicaCount';
export const PHASE_INDEX_PRIORITY: string = 'phaseIndexPriority';

export const PHASE_WAIT_FOR_SNAPSHOT_POLICY = 'waitForSnapshotPolicy';

export const PHASE_ATTRIBUTES_THAT_ARE_NUMBERS_VALIDATE: string[] = [
PHASE_ROLLOVER_MINIMUM_AGE,
PHASE_FORCE_MERGE_SEGMENTS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiDescribedFormGroup, EuiSwitch } from '@elastic/eui';
import {
EuiDescribedFormGroup,
EuiSwitch,
EuiFieldText,
EuiTextColor,
EuiFormRow,
} from '@elastic/eui';

import { PHASE_DELETE, PHASE_ENABLED } from '../../../../constants';
import { ActiveBadge, PhaseErrorMessage } from '../../../components';
import { PHASE_DELETE, PHASE_ENABLED, PHASE_WAIT_FOR_SNAPSHOT_POLICY } from '../../../../constants';
import { ActiveBadge, LearnMoreLink, OptionalLabel, PhaseErrorMessage } from '../../../components';
import { MinAgeInput } from '../min_age_input';

export class DeletePhase extends PureComponent {
Expand Down Expand Up @@ -85,6 +91,48 @@ export class DeletePhase extends PureComponent {
<div />
)}
</EuiDescribedFormGroup>
{phaseData[PHASE_ENABLED] ? (
<EuiDescribedFormGroup
title={
<h3>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotTitle"
defaultMessage="Wait for snapshot policy"
/>
</h3>
}
description={
<EuiTextColor color="subdued">
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotDescription"
defaultMessage="Specify a snapshot policy to be executed before the deletion of the index. This ensures that a snapshot of the deleted index is available."
/>{' '}
<LearnMoreLink docPath="ilm-wait-for-snapshot.html" />
</EuiTextColor>
}
titleSize="xs"
fullWidth
>
<EuiFormRow
id="deletePhaseWaitForSnapshot"
label={
<Fragment>
<FormattedMessage
id="xpack.indexLifecycleMgmt.editPolicy.deletePhase.waitForSnapshotLabel"
defaultMessage="Snapshot policy name"
/>
<OptionalLabel />
</Fragment>
}
>
<EuiFieldText
data-test-subj="waitForSnapshotField"
value={phaseData[PHASE_WAIT_FOR_SNAPSHOT_POLICY]}
onChange={(e) => setPhaseData(PHASE_WAIT_FOR_SNAPSHOT_POLICY, e.target.value)}
/>
</EuiFormRow>
</EuiDescribedFormGroup>
) : null}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
PHASE_ROLLOVER_MINIMUM_AGE,
PHASE_ROLLOVER_MINIMUM_AGE_UNITS,
PHASE_ROLLOVER_ALIAS,
PHASE_WAIT_FOR_SNAPSHOT_POLICY,
} from '../../constants';

export const defaultDeletePhase = {
Expand All @@ -17,5 +18,6 @@ export const defaultDeletePhase = {
[PHASE_ROLLOVER_ALIAS]: '',
[PHASE_ROLLOVER_MINIMUM_AGE]: 0,
[PHASE_ROLLOVER_MINIMUM_AGE_UNITS]: 'd',
[PHASE_WAIT_FOR_SNAPSHOT_POLICY]: '',
};
export const defaultEmptyDeletePhase = defaultDeletePhase;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

export declare const defaultDeletePhase: any;
export declare const defaultColdPhase: any;
export declare const defaultWarmPhase: any;
export declare const defaultHotPhase: any;
Loading

0 comments on commit ce12f5c

Please sign in to comment.