From 9340f5762fceb1b4125fa52dabee01ff8fd08f3d Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 6 Oct 2020 13:02:55 +0200 Subject: [PATCH 01/11] added cloud cold tier specific call out, probably wip --- .../index_lifecycle_management/kibana.json | 1 + .../public/application/index.tsx | 10 +++- .../cloud_data_tier_callout.tsx | 26 +++++++++ .../default_allocation_notice.tsx | 7 +-- .../components/data_tier_allocation/index.ts | 1 + .../no_node_attributes_warning.tsx | 21 +++---- .../shared/data_tier_allocation_field.tsx | 57 ++++++++++++++++--- .../public/plugin.tsx | 5 +- .../public/shared_imports.ts | 10 ++++ .../public/types.ts | 6 ++ 10 files changed, 114 insertions(+), 30 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/shared_imports.ts diff --git a/x-pack/plugins/index_lifecycle_management/kibana.json b/x-pack/plugins/index_lifecycle_management/kibana.json index 479d651fc6698..1b0a73c6a0133 100644 --- a/x-pack/plugins/index_lifecycle_management/kibana.json +++ b/x-pack/plugins/index_lifecycle_management/kibana.json @@ -9,6 +9,7 @@ "features" ], "optionalPlugins": [ + "cloud", "usageCollection", "indexManagement", "home" diff --git a/x-pack/plugins/index_lifecycle_management/public/application/index.tsx b/x-pack/plugins/index_lifecycle_management/public/application/index.tsx index d7812f186a03f..7a7fd20e96c63 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/index.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/index.tsx @@ -8,6 +8,9 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nStart, ScopedHistory, ApplicationStart } from 'kibana/public'; import { UnmountCallback } from 'src/core/public'; +import { CloudSetup } from '../../../cloud/public'; + +import { KibanaContextProvider } from '../shared_imports'; import { App } from './app'; @@ -16,11 +19,14 @@ export const renderApp = ( I18nContext: I18nStart['Context'], history: ScopedHistory, navigateToApp: ApplicationStart['navigateToApp'], - getUrlForApp: ApplicationStart['getUrlForApp'] + getUrlForApp: ApplicationStart['getUrlForApp'], + cloud?: CloudSetup ): UnmountCallback => { render( - + + + , element ); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx new file mode 100644 index 0000000000000..ef0fac684dbc9 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx @@ -0,0 +1,26 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +const i18nTexts = { + title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title', { + defaultMessage: 'No cold tier found', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body', { + defaultMessage: 'Please activate the cold tier in Cloud UI.', + }), +}; + +export const CloudDataTierCallout: FunctionComponent = () => { + return ( + + {i18nTexts.body} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx index 8faa9bb2972c2..8538bf297e269 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx @@ -102,10 +102,5 @@ export const DefaultAllocationNotice: FunctionComponent = ({ phase, targe ); - return ( - <> - - {content} - - ); + return content; }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts index dcbdf960fd380..937e3dd28da97 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts @@ -10,3 +10,4 @@ export { NodeAttrsDetails } from './node_attrs_details'; export { DataTierAllocation } from './data_tier_allocation'; export { DefaultAllocationNotice } from './default_allocation_notice'; export { NoNodeAttributesWarning } from './no_node_attributes_warning'; +export { CloudDataTierCallout } from './cloud_data_tier_callout'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/no_node_attributes_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/no_node_attributes_warning.tsx index ceccc51f95c1f..69185277f64ce 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/no_node_attributes_warning.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/no_node_attributes_warning.tsx @@ -5,7 +5,7 @@ */ import React, { FunctionComponent } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { PhaseWithAllocation } from '../../../../../../common/types'; @@ -38,16 +38,13 @@ export const NoNodeAttributesWarning: FunctionComponent<{ phase: PhaseWithAlloca phase, }) => { return ( - <> - - - {i18nTexts[phase].body} - - + + {i18nTexts[phase].body} + ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index 623d443a1db01..60f582be7da81 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -6,8 +6,9 @@ import React, { FunctionComponent } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; +import { EuiDescribedFormGroup, EuiFormRow, EuiSpacer } from '@elastic/eui'; +import { useKibana } from '../../../../../shared_imports'; import { PhaseWithAllocationAction, PhaseWithAllocation } from '../../../../../../common/types'; import { PhaseValidationErrors } from '../../../../services/policies/policy_validation'; import { getAvailableNodeRoleForPhase } from '../../../../lib/data_tiers'; @@ -18,6 +19,7 @@ import { DefaultAllocationNotice, NoNodeAttributesWarning, NodesDataProvider, + CloudDataTierCallout, } from '../../components/data_tier_allocation'; const i18nTexts = { @@ -46,17 +48,20 @@ export const DataTierAllocationField: FunctionComponent = ({ isShowingErrors, errors, }) => { + const { + services: { cloud }, + } = useKibana(); return ( - {(nodesData) => { - const hasNodeAttrs = Boolean(Object.keys(nodesData.nodesByAttributes ?? {}).length); + {({ nodesByRoles, nodesByAttributes }) => { + const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); const renderDefaultAllocationNotice = () => { if (phaseData.dataTierAllocationType !== 'default') { return null; } - const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesData.nodesByRoles); + const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); if ( allocationNodeRole !== 'none' && isNodeRoleFirstPreference(phase, allocationNodeRole) @@ -64,7 +69,12 @@ export const DataTierAllocationField: FunctionComponent = ({ return null; } - return ; + return ( + <> + + + + ); }; const renderNodeAttributesWarning = () => { @@ -74,7 +84,37 @@ export const DataTierAllocationField: FunctionComponent = ({ if (hasNodeAttrs) { return null; } - return ; + return ( + <> + + + + ); + }; + + const renderCloudCallout = () => { + const isCloudEnabled = cloud?.isCloudEnabled ?? false; + if ( + phase !== 'cold' || + !isCloudEnabled || + phaseData.dataTierAllocationType !== 'default' + ) { + return null; + } + + /** + * Check whether there are any data_cold tier roles + */ + if (nodesByRoles.data_cold?.length) { + return null; + } + + return ( + <> + + + + ); }; return ( @@ -92,12 +132,13 @@ export const DataTierAllocationField: FunctionComponent = ({ setPhaseData={setPhaseData} phaseData={phaseData} isShowingErrors={isShowingErrors} - nodes={nodesData.nodesByAttributes} + nodes={nodesByAttributes} /> - {/* Data tier related warnings */} + {/* Data tier related warnings and call-to-action notices */} {renderDefaultAllocationNotice()} {renderNodeAttributesWarning()} + {renderCloudCallout()} diff --git a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx index 645a78bfc99b8..24ce036c0e058 100644 --- a/x-pack/plugins/index_lifecycle_management/public/plugin.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/plugin.tsx @@ -31,7 +31,7 @@ export class IndexLifecycleManagementPlugin { getStartServices, } = coreSetup; - const { usageCollection, management, indexManagement, home } = plugins; + const { usageCollection, management, indexManagement, home, cloud } = plugins; // Initialize services even if the app isn't mounted, because they're used by index management extensions. initHttp(http); @@ -65,7 +65,8 @@ export class IndexLifecycleManagementPlugin { I18nContext, history, navigateToApp, - getUrlForApp + getUrlForApp, + cloud ); return () => { diff --git a/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts b/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts new file mode 100644 index 0000000000000..d479b821ceefc --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/shared_imports.ts @@ -0,0 +1,10 @@ +/* + * 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 { AppServicesContext } from './types'; +import { useKibana as _useKibana } from '../../../../src/plugins/kibana_react/public'; + +export { KibanaContextProvider } from '../../../../src/plugins/kibana_react/public'; +export const useKibana = () => _useKibana(); diff --git a/x-pack/plugins/index_lifecycle_management/public/types.ts b/x-pack/plugins/index_lifecycle_management/public/types.ts index 65db00f1e68c1..c9b9b063cd45f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/types.ts @@ -8,10 +8,12 @@ import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; import { ManagementSetup } from '../../../../src/plugins/management/public'; import { IndexManagementPluginSetup } from '../../index_management/public'; +import { CloudSetup } from '../../cloud/public'; export interface PluginsDependencies { usageCollection?: UsageCollectionSetup; management: ManagementSetup; + cloud?: CloudSetup; indexManagement?: IndexManagementPluginSetup; home?: HomePublicPluginSetup; } @@ -21,3 +23,7 @@ export interface ClientConfigType { enabled: boolean; }; } + +export interface AppServicesContext { + cloud?: CloudSetup; +} From ad721b53f71ef98c6de720113dc566497dd22610 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 7 Oct 2020 10:26:21 +0200 Subject: [PATCH 02/11] added jest test --- .../__jest__/components/edit_policy.test.tsx | 45 ++++++++++++++++++- .../default_allocation_notice.tsx | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index dfbe19ba21a94..db900ebbef309 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -21,8 +21,10 @@ import { fatalErrorsServiceMock, } from '../../../../../src/core/public/mocks'; import { usageCollectionPluginMock } from '../../../../../src/plugins/usage_collection/public/mocks'; +import { CloudSetup } from '../../../cloud/public'; import { EditPolicy } from '../../public/application/sections/edit_policy/edit_policy'; +import { KibanaContextProvider } from '../../public/shared_imports'; import { init as initHttp } from '../../public/application/services/http'; import { init as initUiMetric } from '../../public/application/services/ui_metric'; import { init as initNotification } from '../../public/application/services/notification'; @@ -148,7 +150,14 @@ const save = (rendered: ReactWrapper) => { describe('edit policy', () => { beforeEach(() => { component = ( - + + + ); ({ http } = editPolicyHelpers.setup()); @@ -679,4 +688,38 @@ describe('edit policy', () => { expectedErrorMessages(rendered, [positiveNumberRequiredMessage]); }); }); + describe('on cloud', () => { + beforeEach(() => { + component = ( + + + + ); + ({ http } = editPolicyHelpers.setup()); + ({ server, httpRequestsMockHelpers } = http); + server.respondImmediately = true; + + httpRequestsMockHelpers.setPoliciesResponse(policies); + }); + test('should show cloud notice cold tier nodes do not exist', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'cloudDataTierCallout').exists()).toBeTruthy(); + // Assert that other notices are not showing + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'noNodeAttributesWarning').exists()).toBeFalsy(); + }); + }); }); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx index 8538bf297e269..42f9e8494a0b3 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { EuiCallOut } from '@elastic/eui'; import { PhaseWithAllocation, NodeDataRole } from '../../../../../../common/types'; From 3308f5f8e80edccdc81d6c6fd457d78172fa1af7 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 7 Oct 2020 11:33:13 +0200 Subject: [PATCH 03/11] rephrase copy --- .../data_tier_allocation/cloud_data_tier_callout.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx index ef0fac684dbc9..802cc5f130092 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx @@ -13,7 +13,8 @@ const i18nTexts = { defaultMessage: 'No cold tier found', }), body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body', { - defaultMessage: 'Please activate the cold tier in Cloud UI.', + defaultMessage: + 'Data in this phase will be allocated to hot and warm tier nodes. Go to the Cloud UI to create a cold tier.', }), }; From 38e6900ff1abc9ce2f23b715d2a7b7e5d1a4d9ed Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 12 Oct 2020 11:02:17 +0200 Subject: [PATCH 04/11] 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 --- .../__jest__/components/edit_policy.test.tsx | 79 +- .../common/types/api.ts | 8 + .../data_tier_allocation.tsx | 61 +- .../components/data_tier_allocation/types.ts | 6 + .../shared/data_tier_allocation_field.tsx | 12 +- .../routes/api/nodes/__jest__/fixtures.ts | 2295 +++++++++++++++++ .../__jest__/register_list_route.test.ts | 113 + .../routes/api/nodes/register_list_route.ts | 53 +- 8 files changed, 2569 insertions(+), 58 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/fixtures.ts create mode 100644 x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index db900ebbef309..8a330fbdc4b9d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -456,6 +456,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -504,6 +505,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: {}, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -516,6 +518,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -528,6 +531,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -577,6 +581,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -635,6 +640,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: {}, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -647,6 +653,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -659,6 +666,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, + isUsingLegacyDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -696,7 +704,7 @@ describe('edit policy', () => { history={history} getUrlForApp={jest.fn()} policies={policies} - policyName={''} + policyName="test" /> ); @@ -706,20 +714,63 @@ describe('edit policy', () => { httpRequestsMockHelpers.setPoliciesResponse(policies); }); - test('should show cloud notice cold tier nodes do not exist', async () => { - http.setupNodeListResponse({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + + describe('with legacy data role config', () => { + test('should hide data tier option on cloud using legacy node role configuration', async () => { + http.setupNodeListResponse({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingLegacyDataRoleConfig: true, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that only the custom and off options exist + findTestSubject(rendered, 'dataTierSelect').simulate('click'); + expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy(); + expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy(); + }); + }); + + describe('with node role config', () => { + test('should show off, custom and data role options on cloud with data roles', async () => { + http.setupNodeListResponse({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingLegacyDataRoleConfig: false, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + findTestSubject(rendered, 'dataTierSelect').simulate('click'); + expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeTruthy(); + expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy(); + expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy(); + }); + + test('should show cloud notice when cold tier nodes do not exist', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingLegacyDataRoleConfig: false, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'cloudDataTierCallout').exists()).toBeTruthy(); + // Assert that other notices are not showing + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'noNodeAttributesWarning').exists()).toBeFalsy(); }); - const rendered = mountWithIntl(component); - noRollover(rendered); - setPolicyName(rendered, 'mypolicy'); - await activatePhase(rendered, 'cold'); - expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(findTestSubject(rendered, 'cloudDataTierCallout').exists()).toBeTruthy(); - // Assert that other notices are not showing - expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); - expect(findTestSubject(rendered, 'noNodeAttributesWarning').exists()).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/common/types/api.ts b/x-pack/plugins/index_lifecycle_management/common/types/api.ts index fcdbdf2c9cc90..6646f6a3e58af 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/api.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/api.ts @@ -9,4 +9,12 @@ import { NodeDataRoleWithCatchAll } from '.'; export interface ListNodesRouteResponse { nodesByAttributes: { [attributePair: string]: string[] }; nodesByRoles: { [role in NodeDataRoleWithCatchAll]?: string[] }; + + /** + * A flag to indicate whether a node is using `settings.node.data` which is the legacy way cloud configured nodes + * to have data (and other) roles. + * + * If this is true, it means the cluster is using legacy cloud configuration for data allocation, not node roles. + */ + isUsingLegacyDataRoleConfig: boolean; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx index 4ec488f95c94d..581cf7bbe711a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiText, EuiFormRow, EuiSpacer, EuiSuperSelect, EuiSuperSelectOption } from '@elastic/eui'; @@ -90,7 +90,20 @@ const i18nTexts = { }; export const DataTierAllocation: FunctionComponent = (props) => { - const { phaseData, setPhaseData, phase, hasNodeAttributes } = props; + const { phaseData, setPhaseData, phase, hasNodeAttributes, disableDataTierOption } = props; + + useEffect(() => { + if (disableDataTierOption && phaseData.dataTierAllocationType === 'default') { + /** + * This is a slight hack because we only know we should disable the "custom" option further + * down the component tree (i.e., before the policy has been deserialized). + * + * We reset the value to "custom" if we deserialized to "default. + */ + setPhaseData('dataTierAllocationType', 'custom'); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return (
@@ -102,21 +115,40 @@ export const DataTierAllocation: FunctionComponent = (props) => { onChange={(value) => setPhaseData('dataTierAllocationType', value)} options={ [ + disableDataTierOption + ? undefined + : { + 'data-test-subj': 'defaultDataAllocationOption', + value: 'default', + inputDisplay: i18nTexts.allocationOptions[phase].default.input, + dropdownDisplay: ( + <> + {i18nTexts.allocationOptions[phase].default.input} + +

+ {i18nTexts.allocationOptions[phase].default.helpText} +

+
+ + ), + }, { - value: 'default', - inputDisplay: i18nTexts.allocationOptions[phase].default.input, + 'data-test-subj': 'customDataAllocationOption', + value: 'custom', + inputDisplay: i18nTexts.allocationOptions[phase].custom.inputDisplay, dropdownDisplay: ( <> - {i18nTexts.allocationOptions[phase].default.input} + {i18nTexts.allocationOptions[phase].custom.inputDisplay}

- {i18nTexts.allocationOptions[phase].default.helpText} + {i18nTexts.allocationOptions[phase].custom.helpText}

), }, { + 'data-test-subj': 'noneDataAllocationOption', value: 'none', inputDisplay: i18nTexts.allocationOptions[phase].none.inputDisplay, dropdownDisplay: ( @@ -130,22 +162,7 @@ export const DataTierAllocation: FunctionComponent = (props) => { ), }, - { - 'data-test-subj': 'customDataAllocationOption', - value: 'custom', - inputDisplay: i18nTexts.allocationOptions[phase].custom.inputDisplay, - dropdownDisplay: ( - <> - {i18nTexts.allocationOptions[phase].custom.inputDisplay} - -

- {i18nTexts.allocationOptions[phase].custom.helpText} -

-
- - ), - }, - ] as SelectOptions[] + ].filter(Boolean) as SelectOptions[] } /> diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/types.ts index d4cb31a3be9e7..d3dd536d97df0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/types.ts @@ -19,4 +19,10 @@ export interface SharedProps { isShowingErrors: boolean; nodes: ListNodesRouteResponse['nodesByAttributes']; hasNodeAttributes: boolean; + /** + * When on Cloud we want to disable the data tier allocation option when we detect that we are not + * using node roles in our Node config yet. See {@link ListNodesRouteResponse} for information about how this is + * detected. + */ + disableDataTierOption: boolean; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index 60f582be7da81..f42c74a808b18 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -51,9 +51,10 @@ export const DataTierAllocationField: FunctionComponent = ({ const { services: { cloud }, } = useKibana(); + return ( - {({ nodesByRoles, nodesByAttributes }) => { + {({ nodesByRoles, nodesByAttributes, isUsingLegacyDataRoleConfig }) => { const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); const renderDefaultAllocationNotice = () => { @@ -95,16 +96,14 @@ export const DataTierAllocationField: FunctionComponent = ({ const renderCloudCallout = () => { const isCloudEnabled = cloud?.isCloudEnabled ?? false; if ( - phase !== 'cold' || !isCloudEnabled || - phaseData.dataTierAllocationType !== 'default' + isUsingLegacyDataRoleConfig || + phaseData.dataTierAllocationType !== 'default' || + phase !== 'cold' ) { return null; } - /** - * Check whether there are any data_cold tier roles - */ if (nodesByRoles.data_cold?.length) { return null; } @@ -133,6 +132,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phaseData={phaseData} isShowingErrors={isShowingErrors} nodes={nodesByAttributes} + disableDataTierOption={!!(isUsingLegacyDataRoleConfig && cloud?.isCloudEnabled)} /> {/* Data tier related warnings and call-to-action notices */} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/fixtures.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/fixtures.ts new file mode 100644 index 0000000000000..e547c3f662432 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/fixtures.ts @@ -0,0 +1,2295 @@ +/* + * 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. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +/** + * The fixtures below are from the "_nodes/settings" endpoint on a 7.9.2 Cloud-created cluster. + */ + +export const cloudNodeSettingsWithLegacy = { + _nodes: { + successful: 5, + failed: 0, + total: 5, + }, + cluster_name: '6ee9547c30214d278d2a63c4de98dea5', + nodes: { + t49k7mdeRIiELuOt_MOZ1g: { + transport_address: '10.47.32.43:19833', + name: 'instance-0000000002', + roles: ['data', 'ingest', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000002.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highstorage.1', + logical_availability_zone: 'zone-0', + data: 'warm', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000002', + master: 'false', + data: 'true', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18120', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18120', + }, + network: { + publish_host: '10.47.32.43', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19833', + }, + profiles: { + client: { + port: '20296', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.43', + host: '10.47.32.43', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000002.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highstorage.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-0', + data: 'warm', + }, + build_type: 'docker', + }, + 'SgaCpsXAQu-oTsP4iLGZWw': { + transport_address: '10.47.32.33:19227', + name: 'tiebreaker-0000000004', + roles: ['master', 'remote_cluster_client', 'voting_only'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'tiebreaker-0000000004.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-b', + region: 'unknown-region', + transform: { + node: 'false', + }, + instance_configuration: 'gcp.master.1', + logical_availability_zone: 'tiebreaker', + data: 'hot', + }, + ml: 'false', + ingest: 'false', + name: 'tiebreaker-0000000004', + master: 'true', + voting_only: 'true', + data: 'false', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18013', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18013', + }, + network: { + publish_host: '10.47.32.33', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19227', + }, + profiles: { + client: { + port: '20281', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.33', + host: '10.47.32.33', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'tiebreaker-0000000004.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-b', + 'transform.node': 'false', + region: 'unknown-region', + instance_configuration: 'gcp.master.1', + 'xpack.installed': 'true', + logical_availability_zone: 'tiebreaker', + data: 'hot', + }, + build_type: 'docker', + }, + 'ZVndRfrfSl-kmEyZgJu0JQ': { + transport_address: '10.47.47.205:19570', + name: 'instance-0000000001', + roles: ['data', 'ingest', 'master', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000001.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highio.1', + logical_availability_zone: 'zone-1', + data: 'hot', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000001', + master: 'true', + data: 'true', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18760', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18760', + }, + network: { + publish_host: '10.47.47.205', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19570', + }, + profiles: { + client: { + port: '20803', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.47.205', + host: '10.47.47.205', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000001.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highio.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-1', + data: 'hot', + }, + build_type: 'docker', + }, + Tx8Xig60SIuitXhY0srD6Q: { + transport_address: '10.47.32.41:19901', + name: 'instance-0000000003', + roles: ['data', 'ingest', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000003.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highstorage.1', + logical_availability_zone: 'zone-1', + data: 'warm', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000003', + master: 'false', + data: 'true', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18977', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18977', + }, + network: { + publish_host: '10.47.32.41', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19901', + }, + profiles: { + client: { + port: '20466', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.41', + host: '10.47.32.41', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000003.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highstorage.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-1', + data: 'warm', + }, + build_type: 'docker', + }, + Qtpmy7aBSIaOZisv9Q92TA: { + transport_address: '10.47.47.203:19498', + name: 'instance-0000000000', + roles: ['data', 'ingest', 'master', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000000.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highio.1', + logical_availability_zone: 'zone-0', + data: 'hot', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000000', + master: 'true', + data: 'true', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18221', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18221', + }, + network: { + publish_host: '10.47.47.203', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19498', + }, + profiles: { + client: { + port: '20535', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.47.203', + host: '10.47.47.203', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000000.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highio.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-0', + data: 'hot', + }, + build_type: 'docker', + }, + }, +}; + +export const cloudNodeSettingsWithoutLegacy = { + _nodes: { + successful: 5, + failed: 0, + total: 5, + }, + cluster_name: '6ee9547c30214d278d2a63c4de98dea5', + nodes: { + t49k7mdeRIiELuOt_MOZ1g: { + transport_address: '10.47.32.43:19833', + name: 'instance-0000000002', + roles: ['data', 'ingest', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000002.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highstorage.1', + logical_availability_zone: 'zone-0', + data: 'warm', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000002', + master: 'false', + data: undefined, + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18120', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18120', + }, + network: { + publish_host: '10.47.32.43', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19833', + }, + profiles: { + client: { + port: '20296', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.43', + host: '10.47.32.43', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000002.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highstorage.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-0', + data: 'warm', + }, + build_type: 'docker', + }, + 'SgaCpsXAQu-oTsP4iLGZWw': { + transport_address: '10.47.32.33:19227', + name: 'tiebreaker-0000000004', + roles: ['master', 'remote_cluster_client', 'voting_only'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'tiebreaker-0000000004.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-b', + region: 'unknown-region', + transform: { + node: 'false', + }, + instance_configuration: 'gcp.master.1', + logical_availability_zone: 'tiebreaker', + data: 'hot', + }, + ml: 'false', + ingest: 'false', + name: 'tiebreaker-0000000004', + master: 'true', + voting_only: 'true', + data: 'false', + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18013', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18013', + }, + network: { + publish_host: '10.47.32.33', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19227', + }, + profiles: { + client: { + port: '20281', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.33', + host: '10.47.32.33', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'tiebreaker-0000000004.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-b', + 'transform.node': 'false', + region: 'unknown-region', + instance_configuration: 'gcp.master.1', + 'xpack.installed': 'true', + logical_availability_zone: 'tiebreaker', + data: 'hot', + }, + build_type: 'docker', + }, + 'ZVndRfrfSl-kmEyZgJu0JQ': { + transport_address: '10.47.47.205:19570', + name: 'instance-0000000001', + roles: ['data', 'ingest', 'master', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000001.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highio.1', + logical_availability_zone: 'zone-1', + data: 'hot', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000001', + master: 'true', + data: undefined, + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18760', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18760', + }, + network: { + publish_host: '10.47.47.205', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19570', + }, + profiles: { + client: { + port: '20803', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.47.205', + host: '10.47.47.205', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000001.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highio.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-1', + data: 'hot', + }, + build_type: 'docker', + }, + Tx8Xig60SIuitXhY0srD6Q: { + transport_address: '10.47.32.41:19901', + name: 'instance-0000000003', + roles: ['data', 'ingest', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000003.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highstorage.1', + logical_availability_zone: 'zone-1', + data: 'warm', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000003', + master: 'false', + data: undefined, + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18977', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18977', + }, + network: { + publish_host: '10.47.32.41', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19901', + }, + profiles: { + client: { + port: '20466', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.32.41', + host: '10.47.32.41', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000003.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-a', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highstorage.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-1', + data: 'warm', + }, + build_type: 'docker', + }, + Qtpmy7aBSIaOZisv9Q92TA: { + transport_address: '10.47.47.203:19498', + name: 'instance-0000000000', + roles: ['data', 'ingest', 'master', 'remote_cluster_client', 'transform'], + settings: { + node: { + attr: { + xpack: { + installed: 'true', + }, + server_name: 'instance-0000000000.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + region: 'unknown-region', + transform: { + node: 'true', + }, + instance_configuration: 'gcp.data.highio.1', + logical_availability_zone: 'zone-0', + data: 'hot', + }, + ml: 'false', + ingest: 'true', + name: 'instance-0000000000', + master: 'true', + data: undefined, + pidfile: '/app/es.pid', + max_local_storage_nodes: '1', + }, + reindex: { + remote: { + whitelist: ['*.io:*', '*.com:*'], + }, + }, + http: { + compression: 'true', + type: 'security4', + max_warning_header_count: '64', + publish_port: '18221', + 'type.default': 'netty4', + max_warning_header_size: '7168b', + port: '18221', + }, + network: { + publish_host: '10.47.47.203', + bind_host: '_site:ipv4_', + }, + xpack: { + monitoring: { + collection: { + enabled: 'false', + }, + history: { + duration: '3d', + }, + }, + license: { + self_generated: { + type: 'trial', + }, + }, + ml: { + enabled: 'true', + }, + ccr: { + enabled: 'false', + }, + notification: { + email: { + account: { + work: { + email_defaults: { + from: 'Watcher Alert ', + }, + smtp: { + host: 'dockerhost', + port: '10025', + }, + }, + }, + }, + }, + security: { + authc: { + token: { + enabled: 'true', + }, + reserved_realm: { + enabled: 'false', + }, + realms: { + native: { + native: { + order: '1', + }, + }, + file: { + found: { + order: '0', + }, + }, + saml: { + 'cloud-saml-kibana-916c269173df465f9826f4471799de42': { + attributes: { + name: 'http://saml.elastic-cloud.com/attributes/name', + groups: 'http://saml.elastic-cloud.com/attributes/roles', + principal: 'http://saml.elastic-cloud.com/attributes/principal', + }, + sp: { + acs: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/api/security/saml/callback', + entity_id: 'ec:2628060457:916c269173df465f9826f4471799de42', + logout: + 'https://916c269173df465f9826f4471799de42.europe-west4.gcp.elastic-cloud.com:9243/logout', + }, + order: '3', + idp: { + entity_id: 'urn:idp-cloud-elastic-co', + metadata: { + path: + '/app/config/cloud-saml-metadata-916c269173df465f9826f4471799de42.xml', + }, + }, + }, + }, + }, + anonymous: { + username: 'anonymous', + authz_exception: 'false', + roles: 'anonymous', + }, + }, + enabled: 'true', + http: { + ssl: { + enabled: 'true', + }, + }, + transport: { + ssl: { + enabled: 'true', + }, + }, + }, + }, + script: { + allowed_types: 'stored,inline', + }, + cluster: { + routing: { + allocation: { + disk: { + threshold_enabled: 'true', + watermark: { + enable_for_single_data_node: 'true', + }, + }, + awareness: { + attributes: 'region,logical_availability_zone', + }, + }, + }, + name: '6ee9547c30214d278d2a63c4de98dea5', + initial_master_nodes: 'instance-0000000000,instance-0000000001,tiebreaker-0000000004', + election: { + strategy: 'supports_voting_only', + }, + indices: { + close: { + enable: 'true', + }, + }, + metadata: { + managed_policies: ['cloud-snapshot-policy'], + managed_repository: 'found-snapshots', + managed_index_templates: '.cloud-', + }, + }, + client: { + type: 'node', + }, + action: { + auto_create_index: 'true', + destructive_requires_name: 'false', + }, + path: { + home: '/usr/share/elasticsearch', + data: ['/app/data'], + logs: '/app/logs', + }, + transport: { + 'type.default': 'netty4', + type: 'security4', + tcp: { + port: '19498', + }, + profiles: { + client: { + port: '20535', + }, + }, + features: { + 'x-pack': 'true', + }, + }, + discovery: { + seed_hosts: [], + seed_providers: 'file', + }, + }, + ip: '10.47.47.203', + host: '10.47.47.203', + version: '7.9.2', + build_flavor: 'default', + build_hash: 'd34da0ea4a966c4e49417f2da2f244e3e97b4e6e', + attributes: { + server_name: 'instance-0000000000.6ee9547c30214d278d2a63c4de98dea5', + availability_zone: 'europe-west4-c', + 'transform.node': 'true', + region: 'unknown-region', + instance_configuration: 'gcp.data.highio.1', + 'xpack.installed': 'true', + logical_availability_zone: 'zone-0', + data: 'hot', + }, + build_type: 'docker', + }, + }, +}; diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts new file mode 100644 index 0000000000000..b7cef671a7ce6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts @@ -0,0 +1,113 @@ +/* + * 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 { convertSettingsIntoLists } from '../register_list_route'; +import { cloudNodeSettingsWithLegacy, cloudNodeSettingsWithoutLegacy } from './fixtures'; + +describe('convertSettingsIntoLists', () => { + it('does not flag legacy config incorrectly', () => { + const result = convertSettingsIntoLists(cloudNodeSettingsWithoutLegacy, []); + expect(result.isUsingLegacyDataRoleConfig).toBe(false); + }); + + it('converts cloud settings into the expected response and detects legacy config', () => { + const result = convertSettingsIntoLists(cloudNodeSettingsWithLegacy, []); + + expect(result.isUsingLegacyDataRoleConfig).toBe(true); + expect(result.nodesByRoles).toEqual({ + data: [ + 't49k7mdeRIiELuOt_MOZ1g', + 'ZVndRfrfSl-kmEyZgJu0JQ', + 'Tx8Xig60SIuitXhY0srD6Q', + 'Qtpmy7aBSIaOZisv9Q92TA', + ], + }); + expect(result.nodesByAttributes).toMatchInlineSnapshot(` + Object { + "availability_zone:europe-west4-a": Array [ + "ZVndRfrfSl-kmEyZgJu0JQ", + "Tx8Xig60SIuitXhY0srD6Q", + ], + "availability_zone:europe-west4-b": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + ], + "availability_zone:europe-west4-c": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "data:hot": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + "ZVndRfrfSl-kmEyZgJu0JQ", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "data:warm": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "Tx8Xig60SIuitXhY0srD6Q", + ], + "instance_configuration:gcp.data.highio.1": Array [ + "ZVndRfrfSl-kmEyZgJu0JQ", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "instance_configuration:gcp.data.highstorage.1": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "Tx8Xig60SIuitXhY0srD6Q", + ], + "instance_configuration:gcp.master.1": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + ], + "logical_availability_zone:tiebreaker": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + ], + "logical_availability_zone:zone-0": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "logical_availability_zone:zone-1": Array [ + "ZVndRfrfSl-kmEyZgJu0JQ", + "Tx8Xig60SIuitXhY0srD6Q", + ], + "region:unknown-region": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "SgaCpsXAQu-oTsP4iLGZWw", + "ZVndRfrfSl-kmEyZgJu0JQ", + "Tx8Xig60SIuitXhY0srD6Q", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "server_name:instance-0000000000.6ee9547c30214d278d2a63c4de98dea5": Array [ + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "server_name:instance-0000000001.6ee9547c30214d278d2a63c4de98dea5": Array [ + "ZVndRfrfSl-kmEyZgJu0JQ", + ], + "server_name:instance-0000000002.6ee9547c30214d278d2a63c4de98dea5": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + ], + "server_name:instance-0000000003.6ee9547c30214d278d2a63c4de98dea5": Array [ + "Tx8Xig60SIuitXhY0srD6Q", + ], + "server_name:tiebreaker-0000000004.6ee9547c30214d278d2a63c4de98dea5": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + ], + "transform.node:false": Array [ + "SgaCpsXAQu-oTsP4iLGZWw", + ], + "transform.node:true": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "ZVndRfrfSl-kmEyZgJu0JQ", + "Tx8Xig60SIuitXhY0srD6Q", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + "xpack.installed:true": Array [ + "t49k7mdeRIiELuOt_MOZ1g", + "SgaCpsXAQu-oTsP4iLGZWw", + "ZVndRfrfSl-kmEyZgJu0JQ", + "Tx8Xig60SIuitXhY0srD6Q", + "Qtpmy7aBSIaOZisv9Q92TA", + ], + } + `); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts index 99df70e7df82d..7cb3b73be9fa0 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts @@ -11,22 +11,27 @@ import { ListNodesRouteResponse, NodeDataRole } from '../../../../common/types'; import { RouteDependencies } from '../../../types'; import { addBasePath } from '../../../services'; -interface Stats { +interface Settings { nodes: { [nodeId: string]: { attributes: Record; roles: string[]; + settings: { + node: { + data?: string; + }; + }; }; }; } -function convertStatsIntoList( - stats: Stats, +export function convertSettingsIntoLists( + settings: Settings, disallowedNodeAttributes: string[] ): ListNodesRouteResponse { - return Object.entries(stats.nodes).reduce( - (accum, [nodeId, nodeStats]) => { - const attributes = nodeStats.attributes || {}; + return Object.entries(settings.nodes).reduce( + (accum, [nodeId, nodeSettings]) => { + const attributes = nodeSettings.attributes || {}; for (const [key, value] of Object.entries(attributes)) { const isNodeAttributeAllowed = !disallowedNodeAttributes.includes(key); if (isNodeAttributeAllowed) { @@ -36,23 +41,36 @@ function convertStatsIntoList( } } - const dataRoles = nodeStats.roles.filter((r) => r.startsWith('data')) as NodeDataRole[]; + const dataRoles = nodeSettings.roles.filter((r) => r.startsWith('data')) as NodeDataRole[]; for (const role of dataRoles) { accum.nodesByRoles[role as NodeDataRole] = accum.nodesByRoles[role] ?? []; accum.nodesByRoles[role as NodeDataRole]!.push(nodeId); } + + // If we detect a single node using legacy "data:true" setting we know we are not using data roles for + // data allocation. + if (!accum.isUsingLegacyDataRoleConfig) { + accum.isUsingLegacyDataRoleConfig = nodeSettings.settings?.node?.data === 'true'; + } return accum; }, - { nodesByAttributes: {}, nodesByRoles: {} } as ListNodesRouteResponse + { + nodesByAttributes: {}, + nodesByRoles: {}, + // Start with assumption that we are not using legacy config + isUsingLegacyDataRoleConfig: false, + } as ListNodesRouteResponse ); } -async function fetchNodeStats(callAsCurrentUser: LegacyAPICaller): Promise { - const params = { - format: 'json', - }; - - return await callAsCurrentUser('nodes.stats', params); +async function fetchNodeSettings(callAsCurrentUser: LegacyAPICaller): Promise { + return await callAsCurrentUser('transport.request', { + method: 'GET', + path: '/_nodes/settings', + query: { + format: 'json', + }, + }); } export function registerListRoute({ router, config, license, lib }: RouteDependencies) { @@ -74,10 +92,13 @@ export function registerListRoute({ router, config, license, lib }: RouteDepende { path: addBasePath('/nodes/list'), validate: false }, license.guardApiRoute(async (context, request, response) => { try { - const stats = await fetchNodeStats( + const settings = await fetchNodeSettings( context.core.elasticsearch.legacy.client.callAsCurrentUser ); - const body: ListNodesRouteResponse = convertStatsIntoList(stats, disallowedNodeAttributes); + const body: ListNodesRouteResponse = convertSettingsIntoLists( + settings, + disallowedNodeAttributes + ); return response.ok({ body }); } catch (e) { if (lib.isEsError(e)) { From 56a4cee1cf000bdb82d6827810ee418173d9e477 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 12 Oct 2020 11:08:27 +0200 Subject: [PATCH 05/11] added test for not-on-cloud case --- .../__jest__/components/edit_policy.test.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index 8a330fbdc4b9d..a1db358c99cad 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -696,6 +696,29 @@ describe('edit policy', () => { expectedErrorMessages(rendered, [positiveNumberRequiredMessage]); }); }); + describe('not on cloud', () => { + beforeEach(() => { + server.respondImmediately = true; + }); + test('should show all allocation options, even if using legacy config', async () => { + http.setupNodeListResponse({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingLegacyDataRoleConfig: true, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that only the custom and off options exist + findTestSubject(rendered, 'dataTierSelect').simulate('click'); + expect(findTestSubject(rendered, 'defaultDataAllocationOption').exists()).toBeTruthy(); + expect(findTestSubject(rendered, 'customDataAllocationOption').exists()).toBeTruthy(); + expect(findTestSubject(rendered, 'noneDataAllocationOption').exists()).toBeTruthy(); + }); + }); describe('on cloud', () => { beforeEach(() => { component = ( From 9cb0c1ede3ab9e5c3ee9b5654be3aa57de61d076 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 12 Oct 2020 11:33:24 +0200 Subject: [PATCH 06/11] Refactored logic for rendering data allocation notices Also updated a comment. --- .../data_tier_allocation.tsx | 4 +- .../shared/data_tier_allocation_field.tsx | 100 +++++++----------- 2 files changed, 43 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx index 581cf7bbe711a..ec1555a3365f4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx @@ -96,9 +96,9 @@ export const DataTierAllocation: FunctionComponent = (props) => { if (disableDataTierOption && phaseData.dataTierAllocationType === 'default') { /** * This is a slight hack because we only know we should disable the "custom" option further - * down the component tree (i.e., before the policy has been deserialized). + * down the component tree (i.e., after the policy has been deserialized). * - * We reset the value to "custom" if we deserialized to "default. + * We reset the value to "custom" if we deserialized to "default". */ setPhaseData('dataTierAllocationType', 'custom'); } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index f42c74a808b18..1e9b0e054af0e 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -57,63 +57,47 @@ export const DataTierAllocationField: FunctionComponent = ({ {({ nodesByRoles, nodesByAttributes, isUsingLegacyDataRoleConfig }) => { const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); - const renderDefaultAllocationNotice = () => { - if (phaseData.dataTierAllocationType !== 'default') { - return null; + const renderNotice = () => { + if (phaseData.dataTierAllocationType === 'default') { + const isCloudEnabled = cloud?.isCloudEnabled ?? false; + if ( + isCloudEnabled && + !isUsingLegacyDataRoleConfig && + phase === 'cold' && + !nodesByRoles.data_cold?.length + ) { + // Tell cloud users they can deploy cold tier nodes. + return ( + <> + + + + ); + } + + const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); + if ( + allocationNodeRole === 'none' || + !isNodeRoleFirstPreference(phase, allocationNodeRole) + ) { + return ( + <> + + + + ); + } + } else if (phaseData.dataTierAllocationType === 'custom') { + if (!hasNodeAttrs) { + return ( + <> + + + + ); + } } - - const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); - if ( - allocationNodeRole !== 'none' && - isNodeRoleFirstPreference(phase, allocationNodeRole) - ) { - return null; - } - - return ( - <> - - - - ); - }; - - const renderNodeAttributesWarning = () => { - if (phaseData.dataTierAllocationType !== 'custom') { - return null; - } - if (hasNodeAttrs) { - return null; - } - return ( - <> - - - - ); - }; - - const renderCloudCallout = () => { - const isCloudEnabled = cloud?.isCloudEnabled ?? false; - if ( - !isCloudEnabled || - isUsingLegacyDataRoleConfig || - phaseData.dataTierAllocationType !== 'default' || - phase !== 'cold' - ) { - return null; - } - - if (nodesByRoles.data_cold?.length) { - return null; - } - - return ( - <> - - - - ); + return null; }; return ( @@ -136,9 +120,7 @@ export const DataTierAllocationField: FunctionComponent = ({ /> {/* Data tier related warnings and call-to-action notices */} - {renderDefaultAllocationNotice()} - {renderNodeAttributesWarning()} - {renderCloudCallout()} + {renderNotice()} From 7b6351eaca685c8f0548b9030ae059b96f699907 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 12 Oct 2020 11:46:11 +0200 Subject: [PATCH 07/11] paraphrashing of Adams copy recommendation --- .../data_tier_allocation/cloud_data_tier_callout.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx index 802cc5f130092..8004186f88ee1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx @@ -10,11 +10,10 @@ import { EuiCallOut } from '@elastic/eui'; const i18nTexts = { title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title', { - defaultMessage: 'No cold tier found', + defaultMessage: 'Create a cold tier', }), body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body', { - defaultMessage: - 'Data in this phase will be allocated to hot and warm tier nodes. Go to the Cloud UI to create a cold tier.', + defaultMessage: 'Edit your deployment on Cloud to create cold tier nodes.', }), }; From 7cee2fd6c80e02efaa83f6f106233313f92ae68f Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 12 Oct 2020 13:30:27 +0200 Subject: [PATCH 08/11] Fix comment --- .../components/data_tier_allocation/data_tier_allocation.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx index ec1555a3365f4..5136b72f83505 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx @@ -95,7 +95,7 @@ export const DataTierAllocation: FunctionComponent = (props) => { useEffect(() => { if (disableDataTierOption && phaseData.dataTierAllocationType === 'default') { /** - * This is a slight hack because we only know we should disable the "custom" option further + * This is a slight hack because we only know we should disable the "default" option further * down the component tree (i.e., after the policy has been deserialized). * * We reset the value to "custom" if we deserialized to "default". From ca92f730c0283d723e872f4277a473503464566e Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 13 Oct 2020 11:10:34 +0200 Subject: [PATCH 09/11] address pr feedback --- .../common/types/api.ts | 6 +- .../cloud_data_tier_callout.tsx | 2 +- .../shared/data_tier_allocation_field.tsx | 87 ++++++++++--------- .../__jest__/register_list_route.test.ts | 8 +- .../routes/api/nodes/register_list_route.ts | 9 +- .../index_lifecycle_management/nodes.js | 1 + 6 files changed, 61 insertions(+), 52 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/common/types/api.ts b/x-pack/plugins/index_lifecycle_management/common/types/api.ts index 6646f6a3e58af..ccdd7fcb11778 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/api.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/api.ts @@ -11,10 +11,10 @@ export interface ListNodesRouteResponse { nodesByRoles: { [role in NodeDataRoleWithCatchAll]?: string[] }; /** - * A flag to indicate whether a node is using `settings.node.data` which is the legacy way cloud configured nodes - * to have data (and other) roles. + * A flag to indicate whether a node is using `settings.node.data` which is the now deprecated way cloud configured + * nodes to have data (and other) roles. * * If this is true, it means the cluster is using legacy cloud configuration for data allocation, not node roles. */ - isUsingLegacyDataRoleConfig: boolean; + isUsingDeprecatedDataRoleConfig: boolean; } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx index 8004186f88ee1..2dff55ac10de1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/cloud_data_tier_callout.tsx @@ -13,7 +13,7 @@ const i18nTexts = { defaultMessage: 'Create a cold tier', }), body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body', { - defaultMessage: 'Edit your deployment on Cloud to create cold tier nodes.', + defaultMessage: 'Edit your Elastic Cloud deployment to set up a cold tier.', }), }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index 1e9b0e054af0e..b3772a6e3ebd4 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -54,50 +54,55 @@ export const DataTierAllocationField: FunctionComponent = ({ return ( - {({ nodesByRoles, nodesByAttributes, isUsingLegacyDataRoleConfig }) => { + {({ nodesByRoles, nodesByAttributes, isUsingDeprecatedDataRoleConfig }) => { const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); const renderNotice = () => { - if (phaseData.dataTierAllocationType === 'default') { - const isCloudEnabled = cloud?.isCloudEnabled ?? false; - if ( - isCloudEnabled && - !isUsingLegacyDataRoleConfig && - phase === 'cold' && - !nodesByRoles.data_cold?.length - ) { - // Tell cloud users they can deploy cold tier nodes. - return ( - <> - - - - ); - } + switch (phaseData.dataTierAllocationType) { + case 'default': + const isCloudEnabled = cloud?.isCloudEnabled ?? false; + const isUsingNodeRoles = !isUsingDeprecatedDataRoleConfig; + if ( + isCloudEnabled && + isUsingNodeRoles && + phase === 'cold' && + !nodesByRoles.data_cold?.length + ) { + // Tell cloud users they can deploy cold tier nodes. + return ( + <> + + + + ); + } - const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); - if ( - allocationNodeRole === 'none' || - !isNodeRoleFirstPreference(phase, allocationNodeRole) - ) { - return ( - <> - - - - ); - } - } else if (phaseData.dataTierAllocationType === 'custom') { - if (!hasNodeAttrs) { - return ( - <> - - - - ); - } + const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); + if ( + allocationNodeRole === 'none' || + !isNodeRoleFirstPreference(phase, allocationNodeRole) + ) { + return ( + <> + + + + ); + } + break; + case 'custom': + if (!hasNodeAttrs) { + return ( + <> + + + + ); + } + break; + default: + return null; } - return null; }; return ( @@ -116,7 +121,9 @@ export const DataTierAllocationField: FunctionComponent = ({ phaseData={phaseData} isShowingErrors={isShowingErrors} nodes={nodesByAttributes} - disableDataTierOption={!!(isUsingLegacyDataRoleConfig && cloud?.isCloudEnabled)} + disableDataTierOption={ + !!(isUsingDeprecatedDataRoleConfig && cloud?.isCloudEnabled) + } /> {/* Data tier related warnings and call-to-action notices */} diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts index b7cef671a7ce6..5adb7763074fe 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/__jest__/register_list_route.test.ts @@ -8,15 +8,15 @@ import { convertSettingsIntoLists } from '../register_list_route'; import { cloudNodeSettingsWithLegacy, cloudNodeSettingsWithoutLegacy } from './fixtures'; describe('convertSettingsIntoLists', () => { - it('does not flag legacy config incorrectly', () => { + it('detects node role config', () => { const result = convertSettingsIntoLists(cloudNodeSettingsWithoutLegacy, []); - expect(result.isUsingLegacyDataRoleConfig).toBe(false); + expect(result.isUsingDeprecatedDataRoleConfig).toBe(false); }); - it('converts cloud settings into the expected response and detects legacy config', () => { + it('converts cloud settings into the expected response and detects deprecated config', () => { const result = convertSettingsIntoLists(cloudNodeSettingsWithLegacy, []); - expect(result.isUsingLegacyDataRoleConfig).toBe(true); + expect(result.isUsingDeprecatedDataRoleConfig).toBe(true); expect(result.nodesByRoles).toEqual({ data: [ 't49k7mdeRIiELuOt_MOZ1g', diff --git a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts index e444f72035c09..bb1679e695e14 100644 --- a/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts +++ b/x-pack/plugins/index_lifecycle_management/server/routes/api/nodes/register_list_route.ts @@ -47,16 +47,17 @@ export function convertSettingsIntoLists( // If we detect a single node using legacy "data:true" setting we know we are not using data roles for // data allocation. - if (!accum.isUsingLegacyDataRoleConfig) { - accum.isUsingLegacyDataRoleConfig = nodeSettings.settings?.node?.data === 'true'; + if (nodeSettings.settings?.node?.data === 'true') { + accum.isUsingDeprecatedDataRoleConfig = true; } + return accum; }, { nodesByAttributes: {}, nodesByRoles: {}, - // Start with assumption that we are not using legacy config - isUsingLegacyDataRoleConfig: false, + // Start with assumption that we are not using deprecated config + isUsingDeprecatedDataRoleConfig: false, } as ListNodesRouteResponse ); } diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js index c859037597821..eb1f0795bb7ca 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js @@ -29,6 +29,7 @@ export default function ({ getService }) { const nodesIds = Object.keys(nodeStats.nodes); const { body } = await loadNodes().expect(200); + expect(body.isUsingLegacyDataRoleConfig).to.eql(false); expect(body.nodesByAttributes[NODE_CUSTOM_ATTRIBUTE]).to.eql(nodesIds); }); }); From 45a938575cb0e586d4c0cf3b90c96277020c905d Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 13 Oct 2020 11:19:51 +0200 Subject: [PATCH 10/11] clarify slight hack comment --- .../components/data_tier_allocation/data_tier_allocation.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx index 5136b72f83505..f58f36fc45a0c 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/data_tier_allocation.tsx @@ -95,10 +95,15 @@ export const DataTierAllocation: FunctionComponent = (props) => { useEffect(() => { if (disableDataTierOption && phaseData.dataTierAllocationType === 'default') { /** + * @TODO * This is a slight hack because we only know we should disable the "default" option further * down the component tree (i.e., after the policy has been deserialized). * * We reset the value to "custom" if we deserialized to "default". + * + * It would be better if we had all the information we needed before deserializing and + * were able to handle this at the deserialization step instead of patching further down + * the component tree - this should be a future refactor. */ setPhaseData('dataTierAllocationType', 'custom'); } From 3003f6172071e1f6f24be9df629aff90fcc2d8a4 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 13 Oct 2020 11:23:23 +0200 Subject: [PATCH 11/11] complete refactor of new flag for tests --- .../__jest__/components/edit_policy.test.tsx | 24 +++++++++---------- .../index_lifecycle_management/nodes.js | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index a1db358c99cad..953e6244b077f 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -456,7 +456,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -505,7 +505,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: {}, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -518,7 +518,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -531,7 +531,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -581,7 +581,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -640,7 +640,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: {}, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -653,7 +653,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -666,7 +666,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -704,7 +704,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: { test: ['123'] }, nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingLegacyDataRoleConfig: true, + isUsingDeprecatedDataRoleConfig: true, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -743,7 +743,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: { test: ['123'] }, nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingLegacyDataRoleConfig: true, + isUsingDeprecatedDataRoleConfig: true, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -764,7 +764,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: { test: ['123'] }, nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); @@ -782,7 +782,7 @@ describe('edit policy', () => { http.setupNodeListResponse({ nodesByAttributes: {}, nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingLegacyDataRoleConfig: false, + isUsingDeprecatedDataRoleConfig: false, }); const rendered = mountWithIntl(component); noRollover(rendered); diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js index eb1f0795bb7ca..3de3a3279f77c 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/nodes.js @@ -29,7 +29,7 @@ export default function ({ getService }) { const nodesIds = Object.keys(nodeStats.nodes); const { body } = await loadNodes().expect(200); - expect(body.isUsingLegacyDataRoleConfig).to.eql(false); + expect(body.isUsingDeprecatedDataRoleConfig).to.eql(false); expect(body.nodesByAttributes[NODE_CUSTOM_ATTRIBUTE]).to.eql(nodesIds); }); });