Skip to content

Commit e80bb7f

Browse files
authored
[7.10] [ILM] Cloud-specific changes for 7.10 (#79650) (#80318)
* [ILM] Cloud-specific changes for 7.10 (#79650) * added cloud cold tier specific call out, probably wip * added jest test * rephrase copy * Added logic for showing/hiding data tier allocation option - Added server-side test for detecting legacy config - Added client-side test for asserting on cloud data tier option is hidden * added test for not-on-cloud case * Refactored logic for rendering data allocation notices Also updated a comment. * paraphrashing of Adams copy recommendation * Fix comment * address pr feedback * clarify slight hack comment * complete refactor of new flag for tests Co-authored-by: Kibana Machine <[email protected]> # Conflicts: # x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts * replace r with (r)
1 parent 5e63ecb commit e80bb7f

File tree

18 files changed

+2748
-91
lines changed

18 files changed

+2748
-91
lines changed

x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import {
2121
fatalErrorsServiceMock,
2222
} from '../../../../../src/core/public/mocks';
2323
import { usageCollectionPluginMock } from '../../../../../src/plugins/usage_collection/public/mocks';
24+
import { CloudSetup } from '../../../cloud/public';
2425

2526
import { EditPolicy } from '../../public/application/sections/edit_policy/edit_policy';
27+
import { KibanaContextProvider } from '../../public/shared_imports';
2628
import { init as initHttp } from '../../public/application/services/http';
2729
import { init as initUiMetric } from '../../public/application/services/ui_metric';
2830
import { init as initNotification } from '../../public/application/services/notification';
@@ -148,7 +150,14 @@ const save = (rendered: ReactWrapper) => {
148150
describe('edit policy', () => {
149151
beforeEach(() => {
150152
component = (
151-
<EditPolicy history={history} getUrlForApp={jest.fn()} policies={policies} policyName={''} />
153+
<KibanaContextProvider services={{ cloud: { isCloudEnabled: false } as CloudSetup }}>
154+
<EditPolicy
155+
history={history}
156+
getUrlForApp={jest.fn()}
157+
policies={policies}
158+
policyName={''}
159+
/>
160+
</KibanaContextProvider>
152161
);
153162

154163
({ http } = editPolicyHelpers.setup());
@@ -447,6 +456,7 @@ describe('edit policy', () => {
447456
http.setupNodeListResponse({
448457
nodesByAttributes: {},
449458
nodesByRoles: { data: ['node1'] },
459+
isUsingDeprecatedDataRoleConfig: false,
450460
});
451461
const rendered = mountWithIntl(component);
452462
noRollover(rendered);
@@ -495,6 +505,7 @@ describe('edit policy', () => {
495505
http.setupNodeListResponse({
496506
nodesByAttributes: {},
497507
nodesByRoles: {},
508+
isUsingDeprecatedDataRoleConfig: false,
498509
});
499510
const rendered = mountWithIntl(component);
500511
noRollover(rendered);
@@ -507,6 +518,7 @@ describe('edit policy', () => {
507518
http.setupNodeListResponse({
508519
nodesByAttributes: {},
509520
nodesByRoles: { data_hot: ['test'], data_cold: ['test'] },
521+
isUsingDeprecatedDataRoleConfig: false,
510522
});
511523
const rendered = mountWithIntl(component);
512524
noRollover(rendered);
@@ -519,6 +531,7 @@ describe('edit policy', () => {
519531
http.setupNodeListResponse({
520532
nodesByAttributes: {},
521533
nodesByRoles: { data: ['test'] },
534+
isUsingDeprecatedDataRoleConfig: false,
522535
});
523536
const rendered = mountWithIntl(component);
524537
noRollover(rendered);
@@ -568,6 +581,7 @@ describe('edit policy', () => {
568581
http.setupNodeListResponse({
569582
nodesByAttributes: {},
570583
nodesByRoles: { data: ['node1'] },
584+
isUsingDeprecatedDataRoleConfig: false,
571585
});
572586
const rendered = mountWithIntl(component);
573587
noRollover(rendered);
@@ -626,6 +640,7 @@ describe('edit policy', () => {
626640
http.setupNodeListResponse({
627641
nodesByAttributes: {},
628642
nodesByRoles: {},
643+
isUsingDeprecatedDataRoleConfig: false,
629644
});
630645
const rendered = mountWithIntl(component);
631646
noRollover(rendered);
@@ -638,6 +653,7 @@ describe('edit policy', () => {
638653
http.setupNodeListResponse({
639654
nodesByAttributes: {},
640655
nodesByRoles: { data_hot: ['test'], data_warm: ['test'] },
656+
isUsingDeprecatedDataRoleConfig: false,
641657
});
642658
const rendered = mountWithIntl(component);
643659
noRollover(rendered);
@@ -650,6 +666,7 @@ describe('edit policy', () => {
650666
http.setupNodeListResponse({
651667
nodesByAttributes: {},
652668
nodesByRoles: { data: ['test'] },
669+
isUsingDeprecatedDataRoleConfig: false,
653670
});
654671
const rendered = mountWithIntl(component);
655672
noRollover(rendered);
@@ -679,4 +696,104 @@ describe('edit policy', () => {
679696
expectedErrorMessages(rendered, [positiveNumberRequiredMessage]);
680697
});
681698
});
699+
describe('not on cloud', () => {
700+
beforeEach(() => {
701+
server.respondImmediately = true;
702+
});
703+
test('should show all allocation options, even if using legacy config', async () => {
704+
http.setupNodeListResponse({
705+
nodesByAttributes: { test: ['123'] },
706+
nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] },
707+
isUsingDeprecatedDataRoleConfig: true,
708+
});
709+
const rendered = mountWithIntl(component);
710+
noRollover(rendered);
711+
setPolicyName(rendered, 'mypolicy');
712+
await activatePhase(rendered, 'warm');
713+
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
714+
715+
// Assert that only the custom and off options exist
716+
findTestSubject(rendered, 'dataTierSelect').simulate('click');
717+
expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeTruthy();
718+
expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy();
719+
expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy();
720+
});
721+
});
722+
describe('on cloud', () => {
723+
beforeEach(() => {
724+
component = (
725+
<KibanaContextProvider services={{ cloud: { isCloudEnabled: true } as CloudSetup }}>
726+
<EditPolicy
727+
history={history}
728+
getUrlForApp={jest.fn()}
729+
policies={policies}
730+
policyName="test"
731+
/>
732+
</KibanaContextProvider>
733+
);
734+
({ http } = editPolicyHelpers.setup());
735+
({ server, httpRequestsMockHelpers } = http);
736+
server.respondImmediately = true;
737+
738+
httpRequestsMockHelpers.setPoliciesResponse(policies);
739+
});
740+
741+
describe('with legacy data role config', () => {
742+
test('should hide data tier option on cloud using legacy node role configuration', async () => {
743+
http.setupNodeListResponse({
744+
nodesByAttributes: { test: ['123'] },
745+
nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] },
746+
isUsingDeprecatedDataRoleConfig: true,
747+
});
748+
const rendered = mountWithIntl(component);
749+
noRollover(rendered);
750+
setPolicyName(rendered, 'mypolicy');
751+
await activatePhase(rendered, 'warm');
752+
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
753+
754+
// Assert that only the custom and off options exist
755+
findTestSubject(rendered, 'dataTierSelect').simulate('click');
756+
expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeFalsy();
757+
expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy();
758+
expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy();
759+
});
760+
});
761+
762+
describe('with node role config', () => {
763+
test('should show off, custom and data role options on cloud with data roles', async () => {
764+
http.setupNodeListResponse({
765+
nodesByAttributes: { test: ['123'] },
766+
nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] },
767+
isUsingDeprecatedDataRoleConfig: false,
768+
});
769+
const rendered = mountWithIntl(component);
770+
noRollover(rendered);
771+
setPolicyName(rendered, 'mypolicy');
772+
await activatePhase(rendered, 'warm');
773+
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
774+
775+
findTestSubject(rendered, 'dataTierSelect').simulate('click');
776+
expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeTruthy();
777+
expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy();
778+
expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy();
779+
});
780+
781+
test('should show cloud notice when cold tier nodes do not exist', async () => {
782+
http.setupNodeListResponse({
783+
nodesByAttributes: {},
784+
nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] },
785+
isUsingDeprecatedDataRoleConfig: false,
786+
});
787+
const rendered = mountWithIntl(component);
788+
noRollover(rendered);
789+
setPolicyName(rendered, 'mypolicy');
790+
await activatePhase(rendered, 'cold');
791+
expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy();
792+
expect(findTestSubject(rendered, 'cloudDataTierCallout').exists()).toBeTruthy();
793+
// Assert that other notices are not showing
794+
expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy();
795+
expect(findTestSubject(rendered, 'noNodeAttributesWarning').exists()).toBeFalsy();
796+
});
797+
});
798+
});
682799
});

x-pack/plugins/index_lifecycle_management/common/types/api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,12 @@ import { NodeDataRoleWithCatchAll } from '.';
99
export interface ListNodesRouteResponse {
1010
nodesByAttributes: { [attributePair: string]: string[] };
1111
nodesByRoles: { [role in NodeDataRoleWithCatchAll]?: string[] };
12+
13+
/**
14+
* A flag to indicate whether a node is using `settings.node.data` which is the now deprecated way cloud configured
15+
* nodes to have data (and other) roles.
16+
*
17+
* If this is true, it means the cluster is using legacy cloud configuration for data allocation, not node roles.
18+
*/
19+
isUsingDeprecatedDataRoleConfig: boolean;
1220
}

x-pack/plugins/index_lifecycle_management/kibana.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"features"
1010
],
1111
"optionalPlugins": [
12+
"cloud",
1213
"usageCollection",
1314
"indexManagement",
1415
"home"

x-pack/plugins/index_lifecycle_management/public/application/index.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import React from 'react';
88
import { render, unmountComponentAtNode } from 'react-dom';
99
import { I18nStart, ScopedHistory, ApplicationStart } from 'kibana/public';
1010
import { UnmountCallback } from 'src/core/public';
11+
import { CloudSetup } from '../../../cloud/public';
12+
13+
import { KibanaContextProvider } from '../shared_imports';
1114

1215
import { App } from './app';
1316

@@ -16,11 +19,14 @@ export const renderApp = (
1619
I18nContext: I18nStart['Context'],
1720
history: ScopedHistory,
1821
navigateToApp: ApplicationStart['navigateToApp'],
19-
getUrlForApp: ApplicationStart['getUrlForApp']
22+
getUrlForApp: ApplicationStart['getUrlForApp'],
23+
cloud?: CloudSetup
2024
): UnmountCallback => {
2125
render(
2226
<I18nContext>
23-
<App history={history} navigateToApp={navigateToApp} getUrlForApp={getUrlForApp} />
27+
<KibanaContextProvider services={{ cloud }}>
28+
<App history={history} navigateToApp={navigateToApp} getUrlForApp={getUrlForApp} />
29+
</KibanaContextProvider>
2430
</I18nContext>,
2531
element
2632
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { i18n } from '@kbn/i18n';
8+
import React, { FunctionComponent } from 'react';
9+
import { EuiCallOut } from '@elastic/eui';
10+
11+
const i18nTexts = {
12+
title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title', {
13+
defaultMessage: 'Create a cold tier',
14+
}),
15+
body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body', {
16+
defaultMessage: 'Edit your Elastic Cloud deployment to set up a cold tier.',
17+
}),
18+
};
19+
20+
export const CloudDataTierCallout: FunctionComponent = () => {
21+
return (
22+
<EuiCallOut title={i18nTexts.title} data-test-subj="cloudDataTierCallout">
23+
{i18nTexts.body}
24+
</EuiCallOut>
25+
);
26+
};

x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import React, { FunctionComponent } from 'react';
7+
import React, { FunctionComponent, useEffect } from 'react';
88
import { i18n } from '@kbn/i18n';
99
import { EuiText, EuiFormRow, EuiSpacer, EuiSuperSelect, EuiSuperSelectOption } from '@elastic/eui';
1010

@@ -90,7 +90,25 @@ const i18nTexts = {
9090
};
9191

9292
export const DataTierAllocation: FunctionComponent<SharedProps> = (props) => {
93-
const { phaseData, setPhaseData, phase, hasNodeAttributes } = props;
93+
const { phaseData, setPhaseData, phase, hasNodeAttributes, disableDataTierOption } = props;
94+
95+
useEffect(() => {
96+
if (disableDataTierOption && phaseData.dataTierAllocationType === 'default') {
97+
/**
98+
* @TODO
99+
* This is a slight hack because we only know we should disable the "default" option further
100+
* down the component tree (i.e., after the policy has been deserialized).
101+
*
102+
* We reset the value to "custom" if we deserialized to "default".
103+
*
104+
* It would be better if we had all the information we needed before deserializing and
105+
* were able to handle this at the deserialization step instead of patching further down
106+
* the component tree - this should be a future refactor.
107+
*/
108+
setPhaseData('dataTierAllocationType', 'custom');
109+
}
110+
// eslint-disable-next-line react-hooks/exhaustive-deps
111+
}, []);
94112

95113
return (
96114
<div data-test-subj={`${phase}-dataTierAllocationControls`}>
@@ -102,21 +120,40 @@ export const DataTierAllocation: FunctionComponent<SharedProps> = (props) => {
102120
onChange={(value) => setPhaseData('dataTierAllocationType', value)}
103121
options={
104122
[
123+
disableDataTierOption
124+
? undefined
125+
: {
126+
'data-test-subj': 'defaultDataAllocationOption',
127+
value: 'default',
128+
inputDisplay: i18nTexts.allocationOptions[phase].default.input,
129+
dropdownDisplay: (
130+
<>
131+
<strong>{i18nTexts.allocationOptions[phase].default.input}</strong>
132+
<EuiText size="s" color="subdued">
133+
<p className="euiTextColor--subdued">
134+
{i18nTexts.allocationOptions[phase].default.helpText}
135+
</p>
136+
</EuiText>
137+
</>
138+
),
139+
},
105140
{
106-
value: 'default',
107-
inputDisplay: i18nTexts.allocationOptions[phase].default.input,
141+
'data-test-subj': 'customDataAllocationOption',
142+
value: 'custom',
143+
inputDisplay: i18nTexts.allocationOptions[phase].custom.inputDisplay,
108144
dropdownDisplay: (
109145
<>
110-
<strong>{i18nTexts.allocationOptions[phase].default.input}</strong>
146+
<strong>{i18nTexts.allocationOptions[phase].custom.inputDisplay}</strong>
111147
<EuiText size="s" color="subdued">
112148
<p className="euiTextColor--subdued">
113-
{i18nTexts.allocationOptions[phase].default.helpText}
149+
{i18nTexts.allocationOptions[phase].custom.helpText}
114150
</p>
115151
</EuiText>
116152
</>
117153
),
118154
},
119155
{
156+
'data-test-subj': 'noneDataAllocationOption',
120157
value: 'none',
121158
inputDisplay: i18nTexts.allocationOptions[phase].none.inputDisplay,
122159
dropdownDisplay: (
@@ -130,22 +167,7 @@ export const DataTierAllocation: FunctionComponent<SharedProps> = (props) => {
130167
</>
131168
),
132169
},
133-
{
134-
'data-test-subj': 'customDataAllocationOption',
135-
value: 'custom',
136-
inputDisplay: i18nTexts.allocationOptions[phase].custom.inputDisplay,
137-
dropdownDisplay: (
138-
<>
139-
<strong>{i18nTexts.allocationOptions[phase].custom.inputDisplay}</strong>
140-
<EuiText size="s" color="subdued">
141-
<p className="euiTextColor--subdued">
142-
{i18nTexts.allocationOptions[phase].custom.helpText}
143-
</p>
144-
</EuiText>
145-
</>
146-
),
147-
},
148-
] as SelectOptions[]
170+
].filter(Boolean) as SelectOptions[]
149171
}
150172
/>
151173
</EuiFormRow>

x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import { i18n } from '@kbn/i18n';
88
import React, { FunctionComponent } from 'react';
9-
import { EuiCallOut, EuiSpacer } from '@elastic/eui';
9+
import { EuiCallOut } from '@elastic/eui';
1010

1111
import { PhaseWithAllocation, NodeDataRole } from '../../../../../../common/types';
1212

@@ -102,10 +102,5 @@ export const DefaultAllocationNotice: FunctionComponent<Props> = ({ phase, targe
102102
</EuiCallOut>
103103
);
104104

105-
return (
106-
<>
107-
<EuiSpacer size="s" />
108-
{content}
109-
</>
110-
);
105+
return content;
111106
};

0 commit comments

Comments
 (0)