From 4c1f6a73bd693d3b8b5977ede6223a76f10c1e02 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 1 Apr 2021 11:38:02 -0700 Subject: [PATCH 01/13] Refine ILM allocation UX. * Provide allocation behavior guidance when the user has selected Custom, but no attributes are available and: ...data nodes are in use, so indices will allocate to data nodes. ...data tiers are available, so indices will allocate to data tiers. ...data tiers are available but not for the given phase's tier, so indices will be allocated to a fallback data tier. ...no data tiers are available, so allocation won't occur (TODO: Check if this is even possible). * Change the label for the Custom > 'Do not modify allocation configuration' option to 'Allocate to available data nodes' to clarify behavior. * Hide the Custom > 'Allocate to available data nodes' option when on Cloud and data nodes are in use. --- .../edit_policy/edit_policy.helpers.tsx | 15 +- .../features/node_allocation.test.ts | 145 ++++++++++++++---- .../common/types/policies.ts | 2 +- .../get_available_node_roles_for_phase.ts | 6 +- .../components/data_tier_allocation.tsx | 1 + .../components/default_allocation_notice.tsx | 112 -------------- .../components/default_allocation_warning.tsx | 72 --------- .../default_to_data_nodes_notice.tsx | 37 +++++ .../default_to_data_tiers_notice.tsx | 44 ++++++ .../components/index.ts | 8 +- .../components/no_node_attributes_warning.tsx | 60 -------- .../components/no_tiers_available_notice.tsx | 59 +++++++ .../components/node_allocation.tsx | 29 +++- .../components/types.ts | 1 + .../will_use_fallback_tier_notice.tsx | 74 +++++++++ .../data_tier_allocation_field.tsx | 97 +++++++++--- .../translations/translations/ja-JP.json | 11 -- .../translations/translations/zh-CN.json | 11 -- 18 files changed, 452 insertions(+), 332 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_notice.tsx delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx delete mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index 122fb83edab45..4259ece0843b0 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -269,6 +269,13 @@ export const setup = async (arg?: { component.update(); }; + const hasDefaultToDataNodesNotice = () => exists(`${phase}-phase.defaultToDataNodesNotice`); + const hasDefaultToDataTiersNotice = () => exists(`${phase}-phase.defaultToDataTiersNotice`); + const hasDefaultAllocationBehaviorNotice = () => + hasDefaultToDataNodesNotice() && hasDefaultToDataTiersNotice(); + const hasNoTiersAvailableNotice = () => exists(`${phase}-phase.noTiersAvailableNotice`); + const hasWillUseFallbackTierNotice = () => exists(`${phase}-phase.willUseFallbackTierNotice`); + return { hasDataTierAllocationControls: () => exists(controlsSelector), openNodeAttributesSection, @@ -292,9 +299,11 @@ export const setup = async (arg?: { component.update(); }, setSelectedNodeAttribute: createFormSetValueAction(nodeAttrsSelector), - hasNoNodeAttrsWarning: () => exists('noNodeAttributesWarning'), - hasDefaultAllocationWarning: () => exists('defaultAllocationWarning'), - hasDefaultAllocationNotice: () => exists('defaultAllocationNotice'), + hasDefaultToDataNodesNotice, + hasDefaultToDataTiersNotice, + hasDefaultAllocationBehaviorNotice, + hasNoTiersAvailableNotice, + hasWillUseFallbackTierNotice, hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), openNodeDetailsFlyout: async () => { await act(async () => { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts index e289991780c04..fe4b657c6f061 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts @@ -61,7 +61,7 @@ describe(' node allocation', () => { expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); }); - test('shows warning instead of node attributes input when none exist', async () => { + test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, @@ -77,9 +77,50 @@ describe(' node allocation', () => { await actions.warm.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.warm.setDataAllocation('node_attrs'); + expect(actions.warm.hasDefaultToDataTiersNotice()).toBeTruthy(); + expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the warm phase', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await act(async () => { + testBed = await setup(); + }); + const { actions, component } = testBed; + + component.update(); + await actions.warm.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeTruthy(); + expect(actions.warm.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await act(async () => { + testBed = await setup(); + }); + const { actions, component } = testBed; + + component.update(); + await actions.warm.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.warm.setDataAllocation('node_attrs'); + expect(actions.warm.hasDefaultToDataNodesNotice()).toBeTruthy(); expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); }); @@ -89,18 +130,18 @@ describe(' node allocation', () => { expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeFalsy(); + expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); expect(actions.warm.getNodeAttributesSelectOptions().length).toBe(2); }); - test('shows view node attributes link when attribute selected and shows flyout when clicked', async () => { + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { const { actions, component } = testBed; await actions.warm.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasNoNodeAttrsWarning()).toBeFalsy(); + expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); expect(actions.warm.hasNodeDetailsFlyout()).toBeFalsy(); @@ -111,7 +152,7 @@ describe(' node allocation', () => { expect(actions.warm.hasNodeDetailsFlyout()).toBeTruthy(); }); - test('shows default allocation warning when no node roles are found', async () => { + test('offers allocation guidance when no node roles are found', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: {}, @@ -127,10 +168,10 @@ describe(' node allocation', () => { await actions.warm.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationWarning()).toBeTruthy(); + expect(actions.warm.hasNoTiersAvailableNotice()).toBeTruthy(); }); - test('when configuring warm phase shows default allocation notice when hot tier exists, but not warm tier', async () => { + test('in the warm phase, offers allocation guidance when allocation will fallback to the hot tier', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, @@ -146,10 +187,10 @@ describe(' node allocation', () => { await actions.warm.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationNotice()).toBeTruthy(); + expect(actions.warm.hasWillUseFallbackTierNotice()).toBeTruthy(); }); - test(`doesn't show default allocation notice when node with "data" role exists`, async () => { + test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, @@ -164,7 +205,7 @@ describe(' node allocation', () => { await actions.warm.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationNotice()).toBeFalsy(); + expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); }); }); @@ -182,7 +223,7 @@ describe(' node allocation', () => { expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); }); - test('shows warning instead of node attributes input when none exist', async () => { + test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data: ['node1'] }, @@ -199,7 +240,49 @@ describe(' node allocation', () => { expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeTruthy(); + expect(actions.cold.hasDefaultToDataTiersNotice()).toBeTruthy(); + expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the cold phase', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_warm: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await act(async () => { + testBed = await setup(); + }); + const { actions, component } = testBed; + + component.update(); + await actions.cold.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.cold.setDataAllocation('node_attrs'); + expect(actions.cold.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await act(async () => { + testBed = await setup(); + }); + const { actions, component } = testBed; + + component.update(); + await actions.cold.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.cold.setDataAllocation('node_attrs'); + expect(actions.cold.hasDefaultToDataNodesNotice()).toBeTruthy(); expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); }); @@ -209,19 +292,19 @@ describe(' node allocation', () => { expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy(); + expect(actions.cold.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); expect(actions.cold.getNodeAttributesSelectOptions().length).toBe(2); }); - test('shows view node attributes link when attribute selected and shows flyout when clicked', async () => { + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { const { actions, component } = testBed; await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasNoNodeAttrsWarning()).toBeFalsy(); + expect(actions.cold.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); expect(actions.cold.hasNodeDetailsFlyout()).toBeFalsy(); @@ -232,7 +315,7 @@ describe(' node allocation', () => { expect(actions.cold.hasNodeDetailsFlyout()).toBeTruthy(); }); - test('shows default allocation warning when no node roles are found', async () => { + test('offers allocation guidance when no node roles are found', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: {}, @@ -248,20 +331,20 @@ describe(' node allocation', () => { await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationWarning()).toBeTruthy(); + expect(actions.cold.hasNoTiersAvailableNotice()).toBeTruthy(); }); [ { nodesByRoles: { data_hot: ['test'] }, - previousActiveRole: 'hot', + fallbackTier: 'hot', }, { nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - previousActiveRole: 'warm', + fallbackTier: 'warm', }, - ].forEach(({ nodesByRoles, previousActiveRole }) => { - test(`shows default allocation notice when ${previousActiveRole} tiers exists, but not cold tier`, async () => { + ].forEach(({ nodesByRoles, fallbackTier }) => { + test(`in the cold phase, offers allocation guidance when allocation will fallback to the ${fallbackTier} tier`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles, @@ -277,14 +360,14 @@ describe(' node allocation', () => { await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationNotice()).toBeTruthy(); - expect(find('defaultAllocationNotice').text()).toContain( - `This policy will move data in the cold phase to ${previousActiveRole} tier nodes` + expect(actions.cold.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(find('willUseFallbackTierNotice').text()).toContain( + `This policy will move data in the cold phase to ${fallbackTier} tier nodes` ); }); }); - test(`doesn't show default allocation notice when node with "data" role exists`, async () => { + test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, nodesByRoles: { data: ['test'] }, @@ -299,7 +382,7 @@ describe(' node allocation', () => { await actions.cold.enable(true); expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasDefaultAllocationNotice()).toBeFalsy(); + expect(actions.cold.hasWillUseFallbackTierNotice()).toBeFalsy(); }); }); @@ -396,8 +479,12 @@ describe(' node allocation', () => { expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); expect(exists('cloudDataTierCallout')).toBeFalsy(); - expect(exists('defaultAllocationNotice')).toBeFalsy(); - expect(exists('defaultAllocationWarning')).toBeFalsy(); + expect( + actions.warm.hasWillUseFallbackTierNotice() || actions.cold.hasWillUseFallbackTierNotice() + ).toBeFalsy(); + expect( + actions.warm.hasNoTiersAvailableNotice() || actions.cold.hasNoTiersAvailableNotice() + ).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index d3fec300d2d5f..7ce8d7a580b92 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -7,7 +7,7 @@ import { Index as IndexInterface } from '../../../index_management/common/types'; -export type PhaseWithAllocation = 'warm' | 'cold' | 'frozen'; +export type PhaseWithAllocation = 'warm' | 'cold'; export interface SerializedPolicy { name: string; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts index f2596cac8b920..d5ba7fb785069 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts @@ -13,8 +13,6 @@ import { import { phaseToNodePreferenceMap } from '../../../../common/constants'; -export type AllocationNodeRole = DataTierRole | 'none'; - /** * Given a phase and current cluster node roles, determine which nodes the phase * will allocate data to. For instance, for the warm phase, with warm @@ -26,7 +24,7 @@ export type AllocationNodeRole = DataTierRole | 'none'; export const getAvailableNodeRoleForPhase = ( phase: PhaseWithAllocation, nodesByRoles: ListNodesRouteResponse['nodesByRoles'] -): AllocationNodeRole => { +): DataTierRole | undefined => { const preferredNodeRoles = phaseToNodePreferenceMap[phase]; // The 'data' role covers all node roles, so if we have at least one node with the data role @@ -35,5 +33,5 @@ export const getAvailableNodeRoleForPhase = ( return preferredNodeRoles[0]; } - return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)) ?? 'none'; + return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx index 254063ac1a9ea..2378ff722979a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx @@ -185,6 +185,7 @@ export const DataTierAllocation: FunctionComponent = (props) => { if (disableDataTierOption && field.value === 'node_roles') { field.setValue('node_attrs'); } + return ( = { - data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { - defaultMessage: 'hot', - }), - data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { - defaultMessage: 'warm', - }), - data_cold: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel', { - defaultMessage: 'cold', - }), - data_frozen: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierFrozenLabel', { - defaultMessage: 'frozen', - }), -}; - -const i18nTexts = { - notice: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm', { - defaultMessage: - 'This policy will move data in the warm phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - }), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold', { - defaultMessage: - 'This policy will move data in the cold phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - }), - }, - frozen: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen.title', - { defaultMessage: 'No nodes assigned to the frozen tier' } - ), - body: (nodeRole: DataTierRole) => - i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.frozen', - { - defaultMessage: - 'This policy will move data in the frozen phase to {tier} tier nodes instead.', - values: { tier: i18nTextsNodeRoleToDataTier[nodeRole] }, - } - ), - }, - }, - warning: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - }, -}; - -interface Props { - phase: PhaseWithAllocation; - targetNodeRole: DataTierRole; -} - -export const DefaultAllocationNotice: FunctionComponent = ({ phase, targetNodeRole }) => { - return ( - - {i18nTexts.notice[phase].body(targetNodeRole)} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx deleted file mode 100644 index 649eb9f2fcb7f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_allocation_warning.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import React, { FunctionComponent } from 'react'; -import { EuiCallOut } from '@elastic/eui'; - -import { PhaseWithAllocation } from '../../../../../../../../../common/types'; - -const i18nTexts = { - warning: { - warm: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the warm tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - cold: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the cold tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - frozen: { - title: i18n.translate( - 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'No nodes assigned to the frozen tier' } - ), - body: i18n.translate( - 'xpack.indexLifecycleMgmt.frozenPhase.dataTier.defaultAllocationNotAvailableBody', - { - defaultMessage: - 'Assign at least one node to the frozen, cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - } - ), - }, - }, -}; - -interface Props { - phase: PhaseWithAllocation; -} - -export const DefaultAllocationWarning: FunctionComponent = ({ phase }) => { - return ( - - {i18nTexts.warning[phase].body} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx new file mode 100644 index 0000000000000..5cb9f97463e23 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; + +const i18nTexts = { + title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesLabel', { + defaultMessage: 'Indices will be allocated to available data nodes', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesDescription', { + defaultMessage: + 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes instead.', + }), +}; + +export const DefaultToDataNodesNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ + phase, +}) => { + return ( + + {i18nTexts.body} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx new file mode 100644 index 0000000000000..8e3be6a702b8f --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; + +const i18nTexts = { + title: { + warm: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', + { defaultMessage: 'Indices will be allocated to the warm tier' } + ), + cold: i18n.translate( + 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', + { defaultMessage: 'Indices will be allocated to the cold tier' } + ), + }, + body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataTiersDescription', { + defaultMessage: + 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes instead.', + }), +}; + +export const DefaultToDataTiersNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ + phase, +}) => { + return ( + + {i18nTexts.body} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts index e9c884a42fa93..3f2330bed9e2a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts @@ -11,11 +11,13 @@ export { NodeAttrsDetails } from './node_attrs_details'; export { DataTierAllocation } from './data_tier_allocation'; -export { DefaultAllocationNotice } from './default_allocation_notice'; +export { WillUseFallbackTierNotice } from './will_use_fallback_tier_notice'; -export { DefaultAllocationWarning } from './default_allocation_warning'; +export { NoTiersAvailableNotice } from './no_tiers_available_notice'; -export { NoNodeAttributesWarning } from './no_node_attributes_warning'; +export { DefaultToDataTiersNotice } from './default_to_data_tiers_notice'; + +export { DefaultToDataNodesNotice } from './default_to_data_nodes_notice'; export { CloudDataTierCallout } from './cloud_data_tier_callout'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx deleted file mode 100644 index e6cadf7049962..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_node_attributes_warning.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FunctionComponent } from 'react'; -import { EuiCallOut } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { PhaseWithAllocation } from '../../../../../../../../../common/types'; - -const i18nTexts = { - title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel', { - defaultMessage: 'No custom node attributes configured', - }), - warm: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, - cold: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, - frozen: { - body: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.frozen.nodeAttributesMissingDescription', - { - defaultMessage: - 'Define custom node attributes in elasticsearch.yml to use attribute-based allocation.', - } - ), - }, -}; - -export const NoNodeAttributesWarning: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ - phase, -}) => { - return ( - - {i18nTexts[phase].body} - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx new file mode 100644 index 0000000000000..e4e9c839dd679 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { PhaseWithAllocation } from '../../../../../../../../../common/types'; + +const i18nTexts = { + warm: { + title: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.noTiersAvailableTitle', { + defaultMessage: 'No nodes assigned to the warm tier', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.noTiersAvailableBody', { + defaultMessage: + 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + }), + }, + cold: { + title: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.noTiersAvailableTitle', { + defaultMessage: 'No nodes assigned to the cold tier', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.noTiersAvailableBody', { + defaultMessage: + 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + }), + }, + frozen: { + title: i18n.translate('xpack.indexLifecycleMgmt.frozenPhase.dataTier.noTiersAvailableTitle', { + defaultMessage: 'No nodes assigned to the frozen tier', + }), + body: i18n.translate('xpack.indexLifecycleMgmt.frozenPhase.dataTier.noTiersAvailableBody', { + defaultMessage: + 'Assign at least one node to the frozen, cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + }), + }, +}; + +interface Props { + phase: PhaseWithAllocation; + isUsingNodeAttributes?: boolean; +} + +export const NoTiersAvailableNotice: FunctionComponent = ({ phase }) => { + return ( + + {i18nTexts[phase].body} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx index f8f77ed909a91..c22f283d264f2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx @@ -34,13 +34,19 @@ const learnMoreLink = ( ); const i18nTexts = { - doNotModifyAllocationOption: i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption', - { defaultMessage: 'Do not modify allocation configuration' } + allocateToDataNodesOption: i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.allocateToDataNodesOption', + { defaultMessage: 'Allocate to available data nodes' } ), }; -export const NodeAllocation: FunctionComponent = ({ phase, nodes, isLoading }) => { +export const NodeAllocation: FunctionComponent = ({ + phase, + nodes, + isLoading, + isCloudEnabled, + disableDataTierOption, +}) => { const allocationNodeAttributePath = `_meta.${phase}.allocationNodeAttribute`; const [formData] = useFormData({ @@ -60,6 +66,17 @@ export const NodeAllocation: FunctionComponent = ({ phase, nodes, i nodeOptions.sort((a, b) => a.value.localeCompare(b.value)); + let nodeAllocationOptions = []; + + // The options to allocate to data tiers or data nodes should be mutually exclusive, but only on + // Cloud. For on-prem deployments, users might still be using both. + if (!isCloudEnabled || disableDataTierOption) { + const allocateToDataNodesOption = { text: i18nTexts.allocateToDataNodesOption, value: '' }; + nodeAllocationOptions.push(allocateToDataNodesOption); + } + + nodeAllocationOptions = nodeAllocationOptions.concat(nodeOptions); + return ( <> @@ -97,9 +114,7 @@ export const NodeAllocation: FunctionComponent = ({ phase, nodes, i ) : undefined, euiFieldProps: { 'data-test-subj': `${phase}-selectedNodeAttrs`, - options: [{ text: i18nTexts.doNotModifyAllocationOption, value: '' }].concat( - nodeOptions - ), + options: nodeAllocationOptions, hasNoInitialSelection: false, isLoading, }, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts index b78ab720618ef..8ff9c52329962 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts @@ -14,6 +14,7 @@ export interface SharedProps { phase: PhaseWithAllocation; nodes: ListNodesRouteResponse['nodesByAttributes']; hasNodeAttributes: boolean; + isCloudEnabled: 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 diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx new file mode 100644 index 0000000000000..cf8213b96f040 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../../common/types'; + +const nodeRoleToFallbackTierMap: Partial> = { + data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { + defaultMessage: 'hot', + }), + data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { + defaultMessage: 'warm', + }), +}; + +const i18nTexts = { + warm: { + title: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.willUseFallbackTierTitle', { + defaultMessage: 'No nodes assigned to the warm tier', + }), + body: (nodeRole: DataTierRole) => + i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.willUseFallbackTierDescription', { + defaultMessage: + 'This policy will move data in the warm phase to {tier} tier nodes instead.', + values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, + }), + }, + cold: { + title: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.willUseFallbackTierTitle', { + defaultMessage: 'No nodes assigned to the cold tier', + }), + body: (nodeRole: DataTierRole) => + i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.willUseFallbackTierDescription', { + defaultMessage: + 'This policy will move data in the cold phase to {tier} tier nodes instead.', + values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, + }), + }, +}; + +const customizeWithNodeAttributeDescription = i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.customizeWithNodeAttributeDescription', + { + defaultMessage: + 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes.', + } +); + +interface Props { + phase: PhaseWithAllocation; + targetNodeRole: DataTierRole; + isUsingNodeAttributes?: boolean; +} + +export const WillUseFallbackTierNotice: FunctionComponent = ({ + isUsingNodeAttributes, + phase, + targetNodeRole, +}) => { + return ( + +

{i18nTexts[phase].body(targetNodeRole)}

+ + {isUsingNodeAttributes &&

{customizeWithNodeAttributeDescription}

} +
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 8c90a738d2c09..217103b0bc1ed 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -11,20 +11,17 @@ import { i18n } from '@kbn/i18n'; import { EuiDescribedFormGroup, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui'; import { useKibana, useFormData } from '../../../../../../../shared_imports'; - -import { PhaseWithAllocation } from '../../../../../../../../common/types'; - +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../common/types'; import { getAvailableNodeRoleForPhase, isNodeRoleFirstPreference } from '../../../../../../lib'; - import { useLoadNodes } from '../../../../../../services/api'; - import { DataTierAllocationType } from '../../../../types'; import { DataTierAllocation, - DefaultAllocationNotice, - DefaultAllocationWarning, - NoNodeAttributesWarning, + WillUseFallbackTierNotice, + NoTiersAvailableNotice, + DefaultToDataNodesNotice, + DefaultToDataTiersNotice, CloudDataTierCallout, LoadingError, } from './components'; @@ -58,31 +55,37 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr const { nodesByRoles, nodesByAttributes, isUsingDeprecatedDataRoleConfig } = data!; - const hasNodeAttrs = Boolean(Object.keys(nodesByAttributes ?? {}).length); + const hasNodeAttributes = Boolean(Object.keys(nodesByAttributes ?? {}).length); const isCloudEnabled = cloud?.isCloudEnabled ?? false; const cloudDeploymentUrl = cloud?.deploymentUrl; + const allocationNodeRoleForPhase = getAvailableNodeRoleForPhase(phase, nodesByRoles); + const noTiersAvailable = allocationNodeRoleForPhase === undefined; + const willUseFallbackTier = + allocationNodeRoleForPhase !== undefined && + !isNodeRoleFirstPreference(phase, allocationNodeRoleForPhase); + const renderNotice = () => { switch (allocationType) { case 'node_roles': /** - * On cloud most users should be using autoscaling which will provision tiers as they are needed. We do not surface any + * On Cloud most users should be using autoscaling which will provision tiers as they are needed. We do not surface any * of the notices below. */ if (isCloudEnabled) { return null; } + /** * Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting * stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even * this fallback will not succeed. */ - const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesByRoles); - if (allocationNodeRole === 'none') { + if (noTiersAvailable) { return ( <> - + ); } @@ -91,26 +94,81 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr * If we are able to fallback to a data tier that does not map to this phase, we show a notice informing the user that their * data will not be assigned to a corresponding tier. */ - if (!isNodeRoleFirstPreference(phase, allocationNodeRole)) { + if (willUseFallbackTier) { return ( <> - + ); } break; + case 'node_attrs': - if (!hasNodeAttrs) { + /** + * If there are no node attributes, advise the user on the default allocation behavior. + */ + if (!hasNodeAttributes) { + /** + * If data nodes are available, default allocation behavior will be to those nodes. + */ + if (isUsingDeprecatedDataRoleConfig) { + return ( + <> + + + + ); + } + + /** + * Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting + * stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even + * this fallback will not succeed. + * NOTE: Can this ever happen? + */ + if (noTiersAvailable) { + return ( + <> + + + + ); + } + + /** + * If we are able to fallback to a data tier that does not map to this phase, we show a notice informing the user that their + * data will not be assigned to a corresponding tier. + */ + if (willUseFallbackTier) { + return ( + <> + + + + ); + } + + /** + * If using node roles, default allocation behavior will be to the preferred and fallback nodes, depending on the phase. + */ return ( <> - + ); } + /** - * Special cloud case: when deprecated data role configuration is in use, it means that this deployment is not using + * Special Cloud case: when deprecated data role configuration is in use, it means that this deployment is not using * the new node role based allocation. We drive users to the cloud console to migrate to node role based allocation * in that case. */ @@ -154,9 +212,10 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr >
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6dc490b4ffc53..7aa6434699ee3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9999,7 +9999,6 @@ "xpack.indexLifecycleMgmt.appTitle": "インデックスライフサイクルポリシー", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "ポリシーの編集", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "インデックスライフサイクル管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody": "ロールに基づく割り当てを使用するには、1 つ以上のノードを、コールド、ウォーム、またはホットティアに割り当てます。使用可能なノードがない場合、ポリシーは割り当てを完了できません。", "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "コールドティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。安価なハードウェアのコールドフェーズにデータを格納します。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "インデックスを凍結", @@ -10013,7 +10012,6 @@ "xpack.indexLifecycleMgmt.editPolicy.cancelButton": "キャンセル", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body": "Elastic Cloud デプロイを編集し、色ティアを設定します。", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "コールドティアを作成", - "xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "コールドフェーズを有効にする", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "データをコールドティアに移動します。これは、検索パフォーマンスよりもコスト削減を優先するように最適化されています。通常、コールドフェーズではデータが読み取り専用です。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "コールドフェーズ", @@ -10031,7 +10029,6 @@ "xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "新しいスナップショットリポジドリを作成", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "データティアオプション", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "ノード属性を選択", - "xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "コールド", "xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel": "ホット", "xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel": "ウォーム", "xpack.indexLifecycleMgmt.editPolicy.daysOptionLabel": "日", @@ -10096,9 +10093,7 @@ "xpack.indexLifecycleMgmt.editPolicy.minutesOptionLabel": "分", "xpack.indexLifecycleMgmt.editPolicy.nanoSecondsOptionLabel": "ナノ秒", "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "ノード属性を使用して、シャード割り当てを制御します。{learnMoreLink}。", - "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "割り当て構成を修正しない", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "ノードデータを読み込めません", - "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel": "カスタムノード属性が構成されていません", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesReloadButton": "再試行", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsLoadingFailedTitle": "ノード属性詳細を読み込めません", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsReloadButton": "再試行", @@ -10147,7 +10142,6 @@ "xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage": "ポリシー名の頭にアンダーラインを使用することはできず、カンマやスペースを含めることもできません。", "xpack.indexLifecycleMgmt.editPolicy.viewNodeDetailsButton": "選択した属性のノードを表示", "xpack.indexLifecycleMgmt.editPolicy.waitForSnapshot.snapshotPolicyFieldLabel": "ポリシー名 (任意) ", - "xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "属性に基づく割り当てを使用するには、elasticsearch.yml でカスタムノード属性を定義します。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "ウォームフェーズを有効にする", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "ノードの再起動後にインデックスを復元する優先順位を設定します。優先順位の高いインデックスは優先順位の低いインデックスよりも先に復元されます。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescription": "データをウォームティアに移動します。これは、インデックスパフォーマンスよりも検索パフォーマンスを優先するように最適化されています。ウォームフェーズでは、データの追加または更新頻度は高くありません。", @@ -10291,12 +10285,7 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "ホットフェーズ", "xpack.indexLifecycleMgmt.timeline.title": "ポリシー概要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "ウォームフェーズ", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "ロールに基づく割り当てを使用するには、1つ以上のノードを、ウォームまたはホットティアに割り当てます。使用可能なノードがない場合、ポリシーは割り当てを完了できません。", "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "ウォームティアに割り当てられているノードがありません", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "このポリシーはコールドフェーズのデータを{tier}ティアノードに移動します。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title": "コールドティアに割り当てられているノードがありません", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "このポリシーはウォームフェーズのデータを{tier}ティアノードに移動します。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "ウォームティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。", "xpack.infra.alerting.alertDropdownTitle": "アラート", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし (グループなし) ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 32574690b13f2..cb958f6b1a005 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10129,7 +10129,6 @@ "xpack.indexLifecycleMgmt.appTitle": "索引生命周期策略", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "编辑策略", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "索引生命周期管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableBody": "至少将一个节点分配到冷层、温层或热层,以使用基于角色的分配。如果没有可用节点,则策略无法完成分配。", "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到冷层的节点", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。将处于冷阶段的数据存储在成本较低的硬件上。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "冻结索引", @@ -10143,7 +10142,6 @@ "xpack.indexLifecycleMgmt.editPolicy.cancelButton": "取消", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.body": "编辑您的 Elastic Cloud 部署以设置冷层。", "xpack.indexLifecycleMgmt.editPolicy.cloudDataTierCallout.title": "创建冷层", - "xpack.indexLifecycleMgmt.editPolicy.cold.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.activateColdPhaseSwitchLabel": "激活冷阶段", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseDescription": "将数据移到经过优化后节省了成本但牺牲了搜索性能的冷层。数据在冷阶段通常为只读。", "xpack.indexLifecycleMgmt.editPolicy.coldPhase.coldPhaseTitle": "冷阶段", @@ -10161,7 +10159,6 @@ "xpack.indexLifecycleMgmt.editPolicy.createSnapshotRepositoryLink": "创建新的快照库", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.allocationFieldLabel": "数据层选项", "xpack.indexLifecycleMgmt.editPolicy.dataTierAllocation.nodeAllocationFieldLabel": "选择节点属性", - "xpack.indexLifecycleMgmt.editPolicy.dataTierColdLabel": "冷", "xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel": "热", "xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel": "温", "xpack.indexLifecycleMgmt.editPolicy.daysOptionLabel": "天", @@ -10226,9 +10223,7 @@ "xpack.indexLifecycleMgmt.editPolicy.minutesOptionLabel": "分钟", "xpack.indexLifecycleMgmt.editPolicy.nanoSecondsOptionLabel": "纳秒", "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.customOption.description": "使用节点属性控制分片分配。{learnMoreLink}。", - "xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.doNotModifyAllocationOption": "切勿修改分配配置", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesLoadingFailedTitle": "无法加载节点数据", - "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesMissingLabel": "未配置定制节点属性", "xpack.indexLifecycleMgmt.editPolicy.nodeAttributesReloadButton": "重试", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsLoadingFailedTitle": "无法加载节点属性详情", "xpack.indexLifecycleMgmt.editPolicy.nodeDetailsReloadButton": "重试", @@ -10277,7 +10272,6 @@ "xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage": "策略名称不能以下划线开头,且不能包含逗号或空格。", "xpack.indexLifecycleMgmt.editPolicy.viewNodeDetailsButton": "查看具有选定属性的节点", "xpack.indexLifecycleMgmt.editPolicy.waitForSnapshot.snapshotPolicyFieldLabel": "策略名称 (可选) ", - "xpack.indexLifecycleMgmt.editPolicy.warm.nodeAttributesMissingDescription": "在 elasticsearch.yml 中定义定制节点属性,以使用基于属性的分配。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.activateWarmPhaseSwitchLabel": "激活温阶段", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.indexPriorityExplanationText": "设置在节点重新启动后恢复索引的优先级。较高优先级的索引会在较低优先级的索引之前恢复。", "xpack.indexLifecycleMgmt.editPolicy.warmPhase.warmPhaseDescription": "将数据移到优化了搜索性能但牺牲了索引性能的温层。在温层不经常添加或更新数据。", @@ -10425,12 +10419,7 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "热阶段", "xpack.indexLifecycleMgmt.timeline.title": "策略摘要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "温阶段", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableBody": "至少将一个节点分配到温层或冷层,以使用基于角色的分配。如果没有可用节点,则策略无法完成分配。", "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到温层的节点", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold": "此策略会改为将冷阶段的数据移到{tier}层节点。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.cold.title": "没有分配到冷层的节点", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm": "此策略会改为将温阶段的数据移到{tier}层节点。", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title": "没有分配到温层的节点", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。", "xpack.infra.alerting.alertDropdownTitle": "告警", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容 (未分组) ", From fe5850bf6420d5ce44d3e8f8e2fd2b5efa5dcf2c Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 1 Apr 2021 16:30:23 -0700 Subject: [PATCH 02/13] Localize disableDataTierOption flag instead of passing it as a prop, to make it easier to reason about. --- .../edit_policy/features/node_allocation.test.ts | 1 + .../components/data_tier_allocation.tsx | 15 ++++++++++++++- .../components/node_allocation.tsx | 11 +++++++---- .../components/types.ts | 7 +------ .../data_tier_allocation_field.tsx | 4 ++-- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts index fe4b657c6f061..fdecbaa1a228c 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts @@ -461,6 +461,7 @@ describe(' node allocation', () => { // Do not show the call-to-action for users to migrate their cluster to use node roles expect(find('cloudDataTierCallout').exists()).toBeFalsy(); }); + test('do not show node allocation specific warnings on cloud', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: { test: ['123'] }, diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx index 2378ff722979a..1eeb32d84f677 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/data_tier_allocation.tsx @@ -161,7 +161,20 @@ const getSelectOptions = (phase: PhaseWithAllocation, disableDataTierOption: boo ].filter(Boolean) as SelectOptions[]; export const DataTierAllocation: FunctionComponent = (props) => { - const { phase, hasNodeAttributes, disableDataTierOption, isLoading } = props; + const { + phase, + hasNodeAttributes, + isCloudEnabled, + isUsingDeprecatedDataRoleConfig, + isLoading, + } = props; + + /** + * 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. + */ + const disableDataTierOption = Boolean(isCloudEnabled && isUsingDeprecatedDataRoleConfig); const dataTierAllocationTypePath = `_meta.${phase}.dataTierAllocationType`; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx index c22f283d264f2..2e1082bfab093 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx @@ -45,7 +45,7 @@ export const NodeAllocation: FunctionComponent = ({ nodes, isLoading, isCloudEnabled, - disableDataTierOption, + isUsingDeprecatedDataRoleConfig, }) => { const allocationNodeAttributePath = `_meta.${phase}.allocationNodeAttribute`; @@ -68,9 +68,12 @@ export const NodeAllocation: FunctionComponent = ({ let nodeAllocationOptions = []; - // The options to allocate to data tiers or data nodes should be mutually exclusive, but only on - // Cloud. For on-prem deployments, users might still be using both. - if (!isCloudEnabled || disableDataTierOption) { + // On Cloud, allocating to data tiers and allocating to data nodes is mutually exclusive. So we + // only let users select this option if they're using data nodes. Otherwise we remove it.r + // + // On prem, users should have the freedom to choose this option, even if they're using node roles. + // So we always give them this option. + if (!isCloudEnabled || isUsingDeprecatedDataRoleConfig) { const allocateToDataNodesOption = { text: i18nTexts.allocateToDataNodesOption, value: '' }; nodeAllocationOptions.push(allocateToDataNodesOption); } diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts index 8ff9c52329962..aba044e1862ef 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/types.ts @@ -15,12 +15,7 @@ export interface SharedProps { nodes: ListNodesRouteResponse['nodesByAttributes']; hasNodeAttributes: boolean; isCloudEnabled: 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; + isUsingDeprecatedDataRoleConfig: boolean; /** * A flag to indicate whether input fields should be showing a loading spinner */ diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 217103b0bc1ed..0a74b0860f59b 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -157,7 +157,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr } /** - * If using node roles, default allocation behavior will be to the preferred and fallback nodes, depending on the phase. + * If using node roles, default allocation behavior will be to the preferred nodes, depending on the phase. */ return ( <> @@ -216,7 +216,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr phase={phase} nodes={nodesByAttributes} isCloudEnabled={isCloudEnabled} - disableDataTierOption={Boolean(isCloudEnabled && isUsingDeprecatedDataRoleConfig)} + isUsingDeprecatedDataRoleConfig={isUsingDeprecatedDataRoleConfig} isLoading={isLoading} /> From 0ebf82474f29f0e4d64813e35a16556f1a4d3932 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Mon, 5 Apr 2021 12:27:35 -0700 Subject: [PATCH 03/13] Clarify the scenario in which the user has selected node_attrs allocation but no data tiers are available. --- .../data_tier_allocation_field/data_tier_allocation_field.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 0a74b0860f59b..4802d38f9f2da 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -127,8 +127,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr /** * Node role allocation moves data in a phase to a corresponding tier of the same name. To prevent policy execution from getting * stuck ILM allocation will fall back to a previous tier if possible. We show the WARNING below to inform a user when even - * this fallback will not succeed. - * NOTE: Can this ever happen? + * this fallback will not succeed, for example if the user only has 'data' node roles, and no `data_` node roles. */ if (noTiersAvailable) { return ( From 67474bcfa0d208083f8b10ccdc5e6b1670ebeef3 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sat, 24 Apr 2021 16:28:20 -0700 Subject: [PATCH 04/13] Remove inapplicable frozen tier from NoTiersAvailableNotice. --- .../components/no_tiers_available_notice.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx index e4e9c839dd679..8e4bdbc1942fb 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx @@ -30,15 +30,6 @@ const i18nTexts = { 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', }), }, - frozen: { - title: i18n.translate('xpack.indexLifecycleMgmt.frozenPhase.dataTier.noTiersAvailableTitle', { - defaultMessage: 'No nodes assigned to the frozen tier', - }), - body: i18n.translate('xpack.indexLifecycleMgmt.frozenPhase.dataTier.noTiersAvailableBody', { - defaultMessage: - 'Assign at least one node to the frozen, cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', - }), - }, }; interface Props { From 1f4ffb22a92db5c5863c4d51310efd782ac5a26f Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sat, 24 Apr 2021 16:40:03 -0700 Subject: [PATCH 05/13] Adjust copy for clarity: data remains on fallback nodes and subtly urge people to use the built-in node roles. --- .../components/default_to_data_nodes_notice.tsx | 4 ++-- .../components/default_to_data_tiers_notice.tsx | 2 +- .../components/no_tiers_available_notice.tsx | 4 ++-- .../components/node_allocation.tsx | 2 +- .../components/will_use_fallback_tier_notice.tsx | 8 +++----- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx index 5cb9f97463e23..dacd7416984fd 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx @@ -13,11 +13,11 @@ import { PhaseWithAllocation } from '../../../../../../../../../common/types'; const i18nTexts = { title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesLabel', { - defaultMessage: 'Indices will be allocated to available data nodes', + defaultMessage: 'Store data on any available data node', }), body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesDescription', { defaultMessage: - 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes instead.', + 'To allocate data to particular data nodes, use role-based allocation or configure custom node attributes in elasticsearch.yml.', }), }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx index 8e3be6a702b8f..030584c582daa 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx @@ -24,7 +24,7 @@ const i18nTexts = { }, body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataTiersDescription', { defaultMessage: - 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes instead.', + 'To control allocation with custom node attributes instead of the built-in node roles, configure node attributes in elasticsearch.yml.', }), }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx index 8e4bdbc1942fb..f84ac017fe8a1 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx @@ -18,7 +18,7 @@ const i18nTexts = { }), body: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.noTiersAvailableBody', { defaultMessage: - 'Assign at least one node to the warm or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + 'To use role-based allocation, assign one or more nodes to the warm or hot tiers. Allocation will fail if there are no available nodes.', }), }, cold: { @@ -27,7 +27,7 @@ const i18nTexts = { }), body: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.noTiersAvailableBody', { defaultMessage: - 'Assign at least one node to the cold, warm, or hot tier to use role-based allocation. The policy will fail to complete allocation if there are no available nodes.', + 'To use role-based allocation, assign one or more nodes to the cold, warm, or hot tiers. Allocation will fail if there are no available nodes.', }), }, }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx index 2e1082bfab093..109ef12a3bd50 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx @@ -36,7 +36,7 @@ const learnMoreLink = ( const i18nTexts = { allocateToDataNodesOption: i18n.translate( 'xpack.indexLifecycleMgmt.editPolicy.nodeAllocation.allocateToDataNodesOption', - { defaultMessage: 'Allocate to available data nodes' } + { defaultMessage: 'Any data node' } ), }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx index cf8213b96f040..6139280e333e8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx @@ -27,8 +27,7 @@ const i18nTexts = { }), body: (nodeRole: DataTierRole) => i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.willUseFallbackTierDescription', { - defaultMessage: - 'This policy will move data in the warm phase to {tier} tier nodes instead.', + defaultMessage: 'If no warm nodes are available, data is stored in the {tier} tier.', values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, }), }, @@ -38,8 +37,7 @@ const i18nTexts = { }), body: (nodeRole: DataTierRole) => i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.willUseFallbackTierDescription', { - defaultMessage: - 'This policy will move data in the cold phase to {tier} tier nodes instead.', + defaultMessage: 'If no cold nodes are available, data is stored in the {tier} tier.', values: { tier: nodeRoleToFallbackTierMap[nodeRole] }, }), }, @@ -49,7 +47,7 @@ const customizeWithNodeAttributeDescription = i18n.translate( 'xpack.indexLifecycleMgmt.editPolicy.customizeWithNodeAttributeDescription', { defaultMessage: - 'Define custom node attributes in elasticsearch.yml to allocate indices based on these attributes.', + 'To allocate data to particular data nodes, use role-based allocation or configure custom node attributes in elasticsearch.yml.', } ); From 8361de4d4e30bb2805e66ecf584426ea4d273cd4 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sat, 24 Apr 2021 18:44:32 -0700 Subject: [PATCH 06/13] Split up node allocation tests to improve discoverability. Refactor test helpers to define suite-specific helper interfaces, to improve maintainability. --- .../edit_policy/edit_policy.helpers.tsx | 150 ++--- .../features/node_allocation.test.ts | 591 ------------------ .../cloud_aware_behavior.helpers.ts | 37 ++ .../cloud_aware_behavior.test.ts | 154 +++++ .../node_allocation/cold_phase.helpers.tsx | 25 + .../node_allocation/cold_phase.test.ts | 211 +++++++ .../general_behavior.helpers.ts | 26 + .../node_allocation/general_behavior.test.ts | 149 +++++ .../node_allocation/warm_phase.helpers.tsx | 25 + .../node_allocation/warm_phase.test.ts | 196 ++++++ .../edit_policy/init_test_bed.tsx | 59 ++ .../helpers/create_enable_phase_action.ts | 17 + .../helpers/create_form_set_value_action.ts | 23 + .../helpers/create_form_toggle_action.ts | 21 + .../helpers/create_node_allocation_actions.ts | 74 +++ .../helpers/global_mocks.tsx | 34 + .../client_integration/helpers/index.ts | 7 + .../helpers/save_policy_action.ts | 19 + .../helpers/set_replicas_action.ts | 23 + 19 files changed, 1136 insertions(+), 705 deletions(-) delete mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index 64fa99b448308..75a13142196f9 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -13,7 +13,6 @@ import { registerTestBed, TestBedConfig } from '@kbn/test/jest'; import { licensingMock } from '../../../../licensing/public/mocks'; import { EditPolicy } from '../../../public/application/sections/edit_policy'; -import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; import { Phases as PolicyPhases } from '../../../common/types'; @@ -21,6 +20,14 @@ import { KibanaContextProvider } from '../../../public/shared_imports'; import { AppServicesContext } from '../../../public/types'; import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; +import { + createEnablePhaseAction, + createNodeAllocationActions, + createFormToggleAction, + createFormSetValueAction, + setReplicas, + savePolicy, +} from '../helpers'; import { TestSubjects } from '../helpers'; import { POLICY_NAME } from './constants'; @@ -97,13 +104,6 @@ export const setup = async (arg?: { const { find, component, form, exists } = testBed; - const createFormToggleAction = (dataTestSubject: string) => async (checked: boolean) => { - await act(async () => { - form.toggleEuiSwitch(dataTestSubject, checked); - }); - component.update(); - }; - const createFormCheckboxAction = (dataTestSubject: string) => async (checked: boolean) => { await act(async () => { form.selectCheckBox(dataTestSubject, checked); @@ -111,15 +111,6 @@ export const setup = async (arg?: { component.update(); }; - function createFormSetValueAction(dataTestSubject: string) { - return async (value: V) => { - await act(async () => { - form.setInputValue(dataTestSubject, value); - }); - component.update(); - }; - } - const setWaitForSnapshotPolicy = async (snapshotPolicyName: string) => { act(() => { find('snapshotPolicyCombobox').simulate('change', [{ label: snapshotPolicyName }]); @@ -127,16 +118,9 @@ export const setup = async (arg?: { component.update(); }; - const savePolicy = async () => { - await act(async () => { - find('savePolicyButton').simulate('click'); - }); - component.update(); - }; - - const toggleDefaultRollover = createFormToggleAction('useDefaultRolloverSwitch'); + const toggleDefaultRollover = createFormToggleAction(testBed, 'useDefaultRolloverSwitch'); - const toggleRollover = createFormToggleAction('rolloverSwitch'); + const toggleRollover = createFormToggleAction(testBed, 'rolloverSwitch'); const setMaxPrimaryShardSize = async (value: string, units?: string) => { await act(async () => { @@ -162,7 +146,7 @@ export const setup = async (arg?: { component.update(); }; - const setMaxDocs = createFormSetValueAction('hot-selectedMaxDocuments'); + const setMaxDocs = createFormSetValueAction(testBed, 'hot-selectedMaxDocuments'); const setMaxAge = async (value: string, units?: string) => { await act(async () => { @@ -178,8 +162,11 @@ export const setup = async (arg?: { const toggleSelector = `${phase}-forceMergeSwitch`; return { forceMergeFieldExists: () => exists(toggleSelector), - toggleForceMerge: createFormToggleAction(toggleSelector), - setForcemergeSegmentsCount: createFormSetValueAction(`${phase}-selectedForceMergeSegments`), + toggleForceMerge: createFormToggleAction(testBed, toggleSelector), + setForcemergeSegmentsCount: createFormSetValueAction( + testBed, + `${phase}-selectedForceMergeSegments` + ), setBestCompression: createFormCheckboxAction(`${phase}-bestCompression`), }; }; @@ -188,46 +175,38 @@ export const setup = async (arg?: { const toggleSelector = `${phase}-indexPrioritySwitch`; return { indexPriorityExists: () => exists(toggleSelector), - toggleIndexPriority: createFormToggleAction(toggleSelector), - setIndexPriority: createFormSetValueAction(`${phase}-indexPriority`), + toggleIndexPriority: createFormToggleAction(testBed, toggleSelector), + setIndexPriority: createFormSetValueAction(testBed, `${phase}-indexPriority`), }; }; - const enable = (phase: Phases) => createFormToggleAction(`enablePhaseSwitch-${phase}`); - const createMinAgeActions = (phase: Phases) => { return { hasMinAgeInput: () => exists(`${phase}-selectedMinimumAge`), - setMinAgeValue: createFormSetValueAction(`${phase}-selectedMinimumAge`), - setMinAgeUnits: createFormSetValueAction(`${phase}-selectedMinimumAgeUnits`), + setMinAgeValue: createFormSetValueAction(testBed, `${phase}-selectedMinimumAge`), + setMinAgeUnits: createFormSetValueAction(testBed, `${phase}-selectedMinimumAgeUnits`), hasRolloverTipOnMinAge: () => exists(`${phase}-rolloverMinAgeInputIconTip`), }; }; - const setReplicas = (phase: Phases) => async (value: string) => { - if (!exists(`${phase}-selectedReplicaCount`)) { - await createFormToggleAction(`${phase}-setReplicasSwitch`)(true); - } - await createFormSetValueAction(`${phase}-selectedReplicaCount`)(value); - }; - const createShrinkActions = (phase: Phases) => { const toggleSelector = `${phase}-shrinkSwitch`; return { shrinkExists: () => exists(toggleSelector), - toggleShrink: createFormToggleAction(toggleSelector), - setShrink: createFormSetValueAction(`${phase}-primaryShardCount`), + toggleShrink: createFormToggleAction(testBed, toggleSelector), + setShrink: createFormSetValueAction(testBed, `${phase}-primaryShardCount`), }; }; - const createSetFreeze = (phase: Phases) => createFormToggleAction(`${phase}-freezeSwitch`); + const createSetFreeze = (phase: Phases) => + createFormToggleAction(testBed, `${phase}-freezeSwitch`); const createFreezeExists = (phase: Phases) => () => exists(`${phase}-freezeSwitch`); const createReadonlyActions = (phase: Phases) => { const toggleSelector = `${phase}-readonlySwitch`; return { readonlyExists: () => exists(toggleSelector), - toggleReadonly: createFormToggleAction(toggleSelector), + toggleReadonly: createFormToggleAction(testBed, toggleSelector), }; }; @@ -236,7 +215,7 @@ export const setup = async (arg?: { const licenseCalloutSelector = `${fieldSelector}.searchableSnapshotDisabledDueToLicense`; const toggleSelector = `${fieldSelector}.searchableSnapshotToggle`; - const toggleSearchableSnapshot = createFormToggleAction(toggleSelector); + const toggleSearchableSnapshot = createFormToggleAction(testBed, toggleSelector); return { searchableSnapshotDisabled: () => exists(licenseCalloutSelector) && find(licenseCalloutSelector).props().disabled === true, @@ -269,63 +248,6 @@ export const setup = async (arg?: { const hasRolloverSettingRequiredCallout = (): boolean => exists('rolloverSettingsRequired'); - const createNodeAllocationActions = (phase: Phases) => { - const controlsSelector = `${phase}-dataTierAllocationControls`; - const dataTierSelector = `${controlsSelector}.dataTierSelect`; - const nodeAttrsSelector = `${phase}-selectedNodeAttrs`; - - const openNodeAttributesSection = async () => { - await act(async () => { - find(dataTierSelector).simulate('click'); - }); - component.update(); - }; - - const hasDefaultToDataNodesNotice = () => exists(`${phase}-phase.defaultToDataNodesNotice`); - const hasDefaultToDataTiersNotice = () => exists(`${phase}-phase.defaultToDataTiersNotice`); - const hasDefaultAllocationBehaviorNotice = () => - hasDefaultToDataNodesNotice() && hasDefaultToDataTiersNotice(); - const hasNoTiersAvailableNotice = () => exists(`${phase}-phase.noTiersAvailableNotice`); - const hasWillUseFallbackTierNotice = () => exists(`${phase}-phase.willUseFallbackTierNotice`); - - return { - hasDataTierAllocationControls: () => exists(controlsSelector), - openNodeAttributesSection, - hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector), - getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'), - setDataAllocation: async (value: DataTierAllocationType) => { - await openNodeAttributesSection(); - - await act(async () => { - switch (value) { - case 'node_roles': - find(`${controlsSelector}.defaultDataAllocationOption`).simulate('click'); - break; - case 'node_attrs': - find(`${controlsSelector}.customDataAllocationOption`).simulate('click'); - break; - default: - find(`${controlsSelector}.noneDataAllocationOption`).simulate('click'); - } - }); - component.update(); - }, - setSelectedNodeAttribute: createFormSetValueAction(nodeAttrsSelector), - hasDefaultToDataNodesNotice, - hasDefaultToDataTiersNotice, - hasDefaultAllocationBehaviorNotice, - hasNoTiersAvailableNotice, - hasWillUseFallbackTierNotice, - hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), - openNodeDetailsFlyout: async () => { - await act(async () => { - find(`${phase}-viewNodeDetailsFlyoutButton`).simulate('click'); - }); - component.update(); - }, - }; - }; - const expectErrorMessages = (expectedMessages: string[]) => { const errorMessages = component.find('.euiFormErrorText'); expect(errorMessages.length).toBe(expectedMessages.length); @@ -355,10 +277,10 @@ export const setup = async (arg?: { ...testBed, runTimers, actions: { - saveAsNewPolicy: createFormToggleAction('saveAsNewSwitch'), - setPolicyName: createFormSetValueAction('policyNameField'), + saveAsNewPolicy: createFormToggleAction(testBed, 'saveAsNewSwitch'), + setPolicyName: createFormSetValueAction(testBed, 'policyNameField'), setWaitForSnapshotPolicy, - savePolicy, + savePolicy: () => savePolicy(testBed), hasGlobalErrorCallout: () => exists('policyFormErrorsCallout'), expectErrorMessages, timeline: { @@ -384,30 +306,30 @@ export const setup = async (arg?: { ...createSearchableSnapshotActions('hot'), }, warm: { - enable: enable('warm'), + enable: createEnablePhaseAction(testBed, 'warm'), ...createMinAgeActions('warm'), - setReplicas: setReplicas('warm'), + setReplicas: (value: string) => setReplicas(testBed, 'warm', value), hasErrorIndicator: () => exists('phaseErrorIndicator-warm'), ...createShrinkActions('warm'), ...createForceMergeActions('warm'), ...createReadonlyActions('warm'), ...createIndexPriorityActions('warm'), - ...createNodeAllocationActions('warm'), + ...createNodeAllocationActions(testBed, 'warm'), }, cold: { - enable: enable('cold'), + enable: createEnablePhaseAction(testBed, 'cold'), ...createMinAgeActions('cold'), - setReplicas: setReplicas('cold'), + setReplicas: (value: string) => setReplicas(testBed, 'cold', value), setFreeze: createSetFreeze('cold'), freezeExists: createFreezeExists('cold'), ...createReadonlyActions('cold'), hasErrorIndicator: () => exists('phaseErrorIndicator-cold'), ...createIndexPriorityActions('cold'), ...createSearchableSnapshotActions('cold'), - ...createNodeAllocationActions('cold'), + ...createNodeAllocationActions(testBed, 'cold'), }, frozen: { - enable: enable('frozen'), + enable: createEnablePhaseAction(testBed, 'frozen'), ...createMinAgeActions('frozen'), hasErrorIndicator: () => exists('phaseErrorIndicator-frozen'), ...createSearchableSnapshotActions('frozen'), diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts deleted file mode 100644 index fdecbaa1a228c..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation.test.ts +++ /dev/null @@ -1,591 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { act } from 'react-dom/test-utils'; -import { setupEnvironment } from '../../helpers/setup_environment'; -import { EditPolicyTestBed, setup } from '../edit_policy.helpers'; -import { - POLICY_WITH_MIGRATE_OFF, - POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, - POLICY_WITH_NODE_ROLE_ALLOCATION, -} from '../constants'; - -describe(' node allocation', () => { - let testBed: EditPolicyTestBed; - const { server, httpRequestsMockHelpers } = setupEnvironment(); - - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - server.restore(); - }); - - beforeEach(async () => { - server.respondImmediately = true; - httpRequestsMockHelpers.setLoadPolicies([]); - httpRequestsMockHelpers.setListNodes({ - nodesByRoles: { data: ['node1'] }, - nodesByAttributes: { 'attribute:true': ['node1'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - httpRequestsMockHelpers.setNodesDetails('attribute:true', [ - { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, - ]); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - describe('warm phase', () => { - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.warm.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasDefaultToDataTiersNotice()).toBeTruthy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the warm phase', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_hot: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: true, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasDefaultToDataNodesNotice()).toBeTruthy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.warm.getNodeAttributesSelectOptions().length).toBe(2); - }); - - test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.warm.setDataAllocation('node_attrs'); - expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.warm.hasNodeAttributesSelect()).toBeTruthy(); - - expect(actions.warm.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.warm.getNodeAttributesSelectOptions().length).toBe(2); - await actions.warm.setSelectedNodeAttribute('attribute:true'); - - await actions.warm.openNodeDetailsFlyout(); - expect(actions.warm.hasNodeDetailsFlyout()).toBeTruthy(); - }); - - test('offers allocation guidance when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasNoTiersAvailableNotice()).toBeTruthy(); - }); - - test('in the warm phase, offers allocation guidance when allocation will fallback to the hot tier', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasWillUseFallbackTierNotice()).toBeTruthy(); - }); - - test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.warm.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.warm.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - }); - }); - - describe('cold phase', () => { - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.cold.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasDefaultToDataTiersNotice()).toBeTruthy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the cold phase', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_warm: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: true, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasDefaultToDataNodesNotice()).toBeTruthy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.cold.getNodeAttributesSelectOptions().length).toBe(2); - }); - - test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; - - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.cold.setDataAllocation('node_attrs'); - expect(actions.cold.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.cold.hasNodeAttributesSelect()).toBeTruthy(); - - expect(actions.cold.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.cold.getNodeAttributesSelectOptions().length).toBe(2); - await actions.cold.setSelectedNodeAttribute('attribute:true'); - - await actions.cold.openNodeDetailsFlyout(); - expect(actions.cold.hasNodeDetailsFlyout()).toBeTruthy(); - }); - - test('offers allocation guidance when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasNoTiersAvailableNotice()).toBeTruthy(); - }); - - [ - { - nodesByRoles: { data_hot: ['test'] }, - fallbackTier: 'hot', - }, - { - nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - fallbackTier: 'warm', - }, - ].forEach(({ nodesByRoles, fallbackTier }) => { - test(`in the cold phase, offers allocation guidance when allocation will fallback to the ${fallbackTier} tier`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles, - isUsingDeprecatedDataRoleConfig: false, - }); - - await act(async () => { - testBed = await setup(); - }); - const { actions, component, find } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(find('willUseFallbackTierNotice').text()).toContain( - `This policy will move data in the cold phase to ${fallbackTier} tier nodes` - ); - }); - }); - - test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component } = testBed; - - component.update(); - await actions.cold.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.cold.hasWillUseFallbackTierNotice()).toBeFalsy(); - }); - }); - - describe('not on cloud', () => { - test('shows all allocation options, even if using legacy config', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - await act(async () => { - testBed = await setup(); - }); - const { actions, component, exists } = testBed; - - component.update(); - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - // Assert that default, custom and 'none' options exist - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeTruthy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - }); - }); - - describe('on cloud', () => { - describe('using legacy data role config', () => { - test('should hide data tier option on cloud', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - // On cloud, if using legacy config there will not be any "data_*" roles set. - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - const { actions, component, exists, find } = testBed; - - component.update(); - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - // Assert that custom and 'none' options exist - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeFalsy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - // Show the call-to-action for users to migrate their cluster to use node roles - expect(find('cloudDataTierCallout').exists()).toBeTruthy(); - }); - }); - - describe('using node role config', () => { - test('shows recommended, custom and "off" options on cloud with data roles', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - testBed.component.update(); - - const { actions, component, exists, find } = testBed; - await actions.warm.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - await actions.warm.openNodeAttributesSection(); - expect(exists('defaultDataAllocationOption')).toBeTruthy(); - expect(exists('customDataAllocationOption')).toBeTruthy(); - expect(exists('noneDataAllocationOption')).toBeTruthy(); - // Do not show the call-to-action for users to migrate their cluster to use node roles - expect(find('cloudDataTierCallout').exists()).toBeFalsy(); - }); - - test('do not show node allocation specific warnings on cloud', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: { test: ['123'] }, - // No nodes with node roles like "data_hot" or "data_warm" - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); - await act(async () => { - testBed = await setup({ appServicesContext: { cloud: { isCloudEnabled: true } } }); - }); - testBed.component.update(); - - const { actions, component, exists } = testBed; - await actions.warm.enable(true); - await actions.cold.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - - expect(exists('cloudDataTierCallout')).toBeFalsy(); - expect( - actions.warm.hasWillUseFallbackTierNotice() || actions.cold.hasWillUseFallbackTierNotice() - ).toBeFalsy(); - expect( - actions.warm.hasNoTiersAvailableNotice() || actions.cold.hasNoTiersAvailableNotice() - ).toBeFalsy(); - }); - }); - }); - - describe('data allocation', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_MIGRATE_OFF]); - httpRequestsMockHelpers.setListNodes({ - nodesByRoles: {}, - nodesByAttributes: { test: ['123'] }, - isUsingDeprecatedDataRoleConfig: false, - }); - httpRequestsMockHelpers.setLoadSnapshotPolicies([]); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('setting node_attr based allocation, but not selecting node attribute', async () => { - const { actions } = testBed; - await actions.warm.setDataAllocation('node_attrs'); - await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; - - expect(warmPhase.actions.migrate).toEqual({ enabled: false }); - }); - - describe('node roles', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ROLE_ALLOCATION]); - httpRequestsMockHelpers.setListNodes({ - isUsingDeprecatedDataRoleConfig: false, - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['123'] }, - }); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('detecting use of the recommended allocation type', () => { - const { find } = testBed; - const selectedDataAllocation = find( - 'warm-dataTierAllocationControls.dataTierSelect' - ).text(); - expect(selectedDataAllocation).toBe('Use warm nodes (recommended)'); - }); - - test('setting replicas serialization', async () => { - const { actions } = testBed; - await actions.warm.setReplicas('123'); - await actions.savePolicy(); - const latestRequest = server.requests[server.requests.length - 1]; - const warmPhaseActions = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm - .actions; - expect(warmPhaseActions).toMatchInlineSnapshot(` - Object { - "allocate": Object { - "number_of_replicas": 123, - }, - } - `); - }); - }); - - describe('node attr and none', () => { - beforeEach(async () => { - httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION]); - httpRequestsMockHelpers.setListNodes({ - isUsingDeprecatedDataRoleConfig: false, - nodesByAttributes: { test: ['123'] }, - nodesByRoles: { data: ['123'] }, - }); - - await act(async () => { - testBed = await setup(); - }); - - const { component } = testBed; - component.update(); - }); - - test('detecting use of the custom allocation type', () => { - const { find } = testBed; - expect(find('warm-dataTierAllocationControls.dataTierSelect').text()).toBe('Custom'); - }); - - test('detecting use of the "off" allocation type', () => { - const { find } = testBed; - expect(find('cold-dataTierAllocationControls.dataTierSelect').text()).toContain('Off'); - }); - }); - }); -}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts new file mode 100644 index 0000000000000..ba376f9cddd93 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.helpers.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TestBedConfig } from '@kbn/test/jest'; + +import { AppServicesContext } from '../../../../../public/types'; +import { createEnablePhaseAction, createNodeAllocationActions } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type CloudNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupCloudNodeAllocation = async (arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; +}) => { + const testBed = await initTestBed(arg); + + return { + ...testBed, + actions: { + warm: { + enable: createEnablePhaseAction(testBed, 'warm'), + ...createNodeAllocationActions(testBed, 'warm'), + }, + cold: { + enable: createEnablePhaseAction(testBed, 'cold'), + ...createNodeAllocationActions(testBed, 'cold'), + }, + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts new file mode 100644 index 0000000000000..06b667f666808 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cloud_aware_behavior.test.ts @@ -0,0 +1,154 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { + CloudNodeAllocationTestBed, + setupCloudNodeAllocation, +} from './cloud_aware_behavior.helpers'; + +describe(' node allocation cloud-aware behavior', () => { + let testBed: CloudNodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async (isOnCloud?: boolean) => { + await act(async () => { + if (Boolean(isOnCloud)) { + testBed = await setupCloudNodeAllocation({ + appServicesContext: { cloud: { isCloudEnabled: true } }, + }); + } else { + testBed = await setupCloudNodeAllocation(); + } + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + describe('when not on cloud', () => { + test('shows all allocation options, even if using legacy config', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + + await setup(); + const { actions, component, exists } = testBed; + + component.update(); + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that default, custom and 'none' options exist + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeTruthy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + }); + }); + + describe('when on cloud', () => { + describe('using legacy data role config', () => { + test('should hide data tier option on cloud', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + // On cloud, if using legacy config there will not be any "data_*" roles set. + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + await setup(true); + const { actions, component, exists, find } = testBed; + + component.update(); + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + // Assert that custom and 'none' options exist + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeFalsy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + // Show the call-to-action for users to migrate their cluster to use node roles + expect(find('cloudDataTierCallout').exists()).toBeTruthy(); + }); + }); + + describe('using node role config', () => { + test('shows recommended, custom and "off" options on cloud with data roles', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['test'], data_hot: ['test'], data_warm: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(true); + testBed.component.update(); + + const { actions, component, exists, find } = testBed; + await actions.warm.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + await actions.warm.openNodeAttributesSection(); + expect(exists('defaultDataAllocationOption')).toBeTruthy(); + expect(exists('customDataAllocationOption')).toBeTruthy(); + expect(exists('noneDataAllocationOption')).toBeTruthy(); + // Do not show the call-to-action for users to migrate their cluster to use node roles + expect(find('cloudDataTierCallout').exists()).toBeFalsy(); + }); + + test('do not show node allocation specific warnings on cloud', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: { test: ['123'] }, + // No nodes with node roles like "data_hot" or "data_warm" + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(true); + testBed.component.update(); + + const { actions, component, exists } = testBed; + await actions.warm.enable(true); + await actions.cold.enable(true); + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + + expect(exists('cloudDataTierCallout')).toBeFalsy(); + expect( + actions.warm.hasWillUseFallbackTierNotice() || actions.cold.hasWillUseFallbackTierNotice() + ).toBeFalsy(); + expect( + actions.warm.hasNoTiersAvailableNotice() || actions.cold.hasNoTiersAvailableNotice() + ).toBeFalsy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx new file mode 100644 index 0000000000000..52593e67df768 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createEnablePhaseAction, createNodeAllocationActions } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupColdPhaseNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + enable: createEnablePhaseAction(testBed, 'cold'), + ...createNodeAllocationActions(testBed, 'cold'), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts new file mode 100644 index 0000000000000..30b229834dedd --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { NodeAllocationTestBed, setupColdPhaseNodeAllocation } from './cold_phase.helpers'; + +describe(' node allocation in the cold phase', () => { + let testBed: NodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupColdPhaseNodeAllocation(); + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; + + const { actions, component } = testBed; + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + + expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the cold phase', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_warm: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('shows node attributes input when attributes exist', async () => { + const { actions, component } = testBed; + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); + + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions, component } = testBed; + + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); + + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + + test('offers allocation guidance when no node roles are found', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); + }); + + [ + { + nodesByRoles: { data_hot: ['test'] }, + fallbackTier: 'hot', + }, + { + nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + fallbackTier: 'warm', + }, + ].forEach(({ nodesByRoles, fallbackTier }) => { + test(`in the cold phase, offers allocation guidance when allocation will fallback to the ${fallbackTier} tier`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component, find } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(find('willUseFallbackTierNotice').text()).toContain( + `No nodes assigned to the cold tierIf no cold nodes are available, data is stored in the ${fallbackTier} tier.` + ); + }); + }); + + test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts new file mode 100644 index 0000000000000..8dd5a75ac0d22 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.helpers.ts @@ -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 + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createNodeAllocationActions, savePolicy, setReplicas } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type GeneralNodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupGeneralNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + ...createNodeAllocationActions(testBed, 'warm'), + savePolicy: () => savePolicy(testBed), + setReplicas: (value: string) => setReplicas(testBed, 'warm', value), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts new file mode 100644 index 0000000000000..5f32b8b00c387 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { + GeneralNodeAllocationTestBed, + setupGeneralNodeAllocation, +} from './general_behavior.helpers'; +import { + POLICY_WITH_MIGRATE_OFF, + POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION, + POLICY_WITH_NODE_ROLE_ALLOCATION, +} from '../../constants'; + +describe(' node allocation general behavior', () => { + let testBed: GeneralNodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupGeneralNodeAllocation(); + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + describe('data allocation', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_MIGRATE_OFF]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: {}, + nodesByAttributes: { test: ['123'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + httpRequestsMockHelpers.setLoadSnapshotPolicies([]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('setting node_attr based allocation, but not selecting node attribute', async () => { + const { actions } = testBed; + await actions.setDataAllocation('node_attrs'); + await actions.savePolicy(); + const latestRequest = server.requests[server.requests.length - 1]; + const warmPhase = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm; + + expect(warmPhase.actions.migrate).toEqual({ enabled: false }); + }); + + describe('node roles', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ROLE_ALLOCATION]); + httpRequestsMockHelpers.setListNodes({ + isUsingDeprecatedDataRoleConfig: false, + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['123'] }, + }); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('detecting use of the recommended allocation type', () => { + const { find } = testBed; + const selectedDataAllocation = find( + 'warm-dataTierAllocationControls.dataTierSelect' + ).text(); + expect(selectedDataAllocation).toBe('Use warm nodes (recommended)'); + }); + + test('setting replicas serialization', async () => { + const { actions } = testBed; + await actions.setReplicas('123'); + await actions.savePolicy(); + const latestRequest = server.requests[server.requests.length - 1]; + const warmPhaseActions = JSON.parse(JSON.parse(latestRequest.requestBody).body).phases.warm + .actions; + expect(warmPhaseActions).toMatchInlineSnapshot(` + Object { + "allocate": Object { + "number_of_replicas": 123, + }, + } + `); + }); + }); + + describe('node attr and none', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_NODE_ATTR_AND_OFF_ALLOCATION]); + httpRequestsMockHelpers.setListNodes({ + isUsingDeprecatedDataRoleConfig: false, + nodesByAttributes: { test: ['123'] }, + nodesByRoles: { data: ['123'] }, + }); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('detecting use of the custom allocation type', () => { + const { find } = testBed; + expect(find('warm-dataTierAllocationControls.dataTierSelect').text()).toBe('Custom'); + }); + + test('detecting use of the "off" allocation type', () => { + const { find } = testBed; + expect(find('cold-dataTierAllocationControls.dataTierSelect').text()).toContain('Off'); + }); + }); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx new file mode 100644 index 0000000000000..e39629da79cd8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createNodeAllocationActions, createEnablePhaseAction } from '../../../helpers'; +import { initTestBed } from '../../init_test_bed'; + +type SetupReturn = ReturnType; + +export type NodeAllocationTestBed = SetupReturn extends Promise ? U : SetupReturn; + +export const setupWarmPhaseNodeAllocation = async () => { + const testBed = await initTestBed(); + + return { + ...testBed, + actions: { + enable: createEnablePhaseAction(testBed, 'warm'), + ...createNodeAllocationActions(testBed, 'warm'), + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts new file mode 100644 index 0000000000000..a8ab57c21df61 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { setupEnvironment } from '../../../helpers/setup_environment'; +import { NodeAllocationTestBed, setupWarmPhaseNodeAllocation } from './warm_phase.helpers'; + +describe(' node allocation in the warm phase', () => { + let testBed: NodeAllocationTestBed; + const { server, httpRequestsMockHelpers } = setupEnvironment(); + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + server.restore(); + }); + + const setup = async () => { + await act(async () => { + testBed = await setupWarmPhaseNodeAllocation(); + }); + }; + + beforeEach(async () => { + server.respondImmediately = true; + httpRequestsMockHelpers.setLoadPolicies([]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: { data: ['node1'] }, + nodesByAttributes: { 'attribute:true': ['node1'] }, + isUsingDeprecatedDataRoleConfig: true, + }); + httpRequestsMockHelpers.setNodesDetails('attribute:true', [ + { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, + ]); + + await setup(); + + const { component } = testBed; + component.update(); + }); + + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; + + const { actions, component } = testBed; + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + + expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the warm phase', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }); + + test('shows node attributes input when attributes exist', async () => { + const { actions, component } = testBed; + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); + + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions, component } = testBed; + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); + + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + + test('offers allocation guidance when no node roles are found', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); + }); + + test('in the warm phase, offers allocation guidance when allocation will fallback to the hot tier', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + }); + + test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx new file mode 100644 index 0000000000000..f54f86b8d513d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { registerTestBed, TestBedConfig } from '@kbn/test/jest'; + +import '../helpers/global_mocks'; + +import { licensingMock } from '../../../../licensing/public/mocks'; +import { EditPolicy } from '../../../public/application/sections/edit_policy'; +import { KibanaContextProvider } from '../../../public/shared_imports'; +import { AppServicesContext } from '../../../public/types'; +import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; +import { TestSubjects } from '../helpers'; +import { POLICY_NAME } from './constants'; + +const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { + return { + memoryRouter: { + initialEntries: [`/policies/edit/${POLICY_NAME}`], + componentRoutePath: `/policies/edit/:policyName`, + }, + defaultProps: { + getUrlForApp: () => {}, + }, + ...testBedConfigArgs, + }; +}; + +const breadcrumbService = createBreadcrumbsMock(); + +const EditPolicyContainer = ({ appServicesContext, ...rest }: any) => { + return ( + + + + ); +}; + +export const initTestBed = (arg?: { + appServicesContext?: Partial; + testBedConfig?: Partial; +}) => { + const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; + return registerTestBed( + EditPolicyContainer, + getTestBedConfig(testBedConfigArgs) + )(rest); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts new file mode 100644 index 0000000000000..9f6642b991f92 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TestBed } from '@kbn/test/jest'; + +import { Phases as PolicyPhases } from '../../../common/types'; +import { createFormToggleAction } from './create_form_toggle_action'; + +type Phases = keyof PolicyPhases; + +export const createEnablePhaseAction = (testBed: TestBed, phase: Phases) => { + return createFormToggleAction(testBed, `enablePhaseSwitch-${phase}`); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts new file mode 100644 index 0000000000000..95e90ac04192b --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_set_value_action.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export function createFormSetValueAction( + testBed: TestBed, + dataTestSubject: string +) { + const { form, component } = testBed; + + return async (value: V) => { + await act(async () => { + form.setInputValue(dataTestSubject, value); + }); + component.update(); + }; +} diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts new file mode 100644 index 0000000000000..579bb661871da --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_form_toggle_action.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export const createFormToggleAction = (testBed: TestBed, dataTestSubject: string) => async ( + checked: boolean +) => { + const { form, component } = testBed; + + await act(async () => { + form.toggleEuiSwitch(dataTestSubject, checked); + }); + + component.update(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts new file mode 100644 index 0000000000000..331494c47a82c --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +import { Phases as PolicyPhases } from '../../../common/types'; +import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; +import { createFormSetValueAction } from './create_form_set_value_action'; + +type Phases = keyof PolicyPhases; + +export const createNodeAllocationActions = (testBed: TestBed, phase: Phases) => { + const { component, find, exists } = testBed; + + const controlsSelector = `${phase}-dataTierAllocationControls`; + const dataTierSelector = `${controlsSelector}.dataTierSelect`; + const nodeAttrsSelector = `${phase}-selectedNodeAttrs`; + + const openNodeAttributesSection = async () => { + await act(async () => { + find(dataTierSelector).simulate('click'); + }); + component.update(); + }; + + const hasDefaultToDataNodesNotice = () => exists(`${phase}-phase.defaultToDataNodesNotice`); + const hasDefaultToDataTiersNotice = () => exists(`${phase}-phase.defaultToDataTiersNotice`); + const hasDefaultAllocationBehaviorNotice = () => + hasDefaultToDataNodesNotice() && hasDefaultToDataTiersNotice(); + const hasNoTiersAvailableNotice = () => exists(`${phase}-phase.noTiersAvailableNotice`); + const hasWillUseFallbackTierNotice = () => exists(`${phase}-phase.willUseFallbackTierNotice`); + + return { + hasDataTierAllocationControls: () => exists(controlsSelector), + openNodeAttributesSection, + hasNodeAttributesSelect: (): boolean => exists(nodeAttrsSelector), + getNodeAttributesSelectOptions: () => find(nodeAttrsSelector).find('option'), + setDataAllocation: async (value: DataTierAllocationType) => { + await openNodeAttributesSection(); + + await act(async () => { + switch (value) { + case 'node_roles': + find(`${controlsSelector}.defaultDataAllocationOption`).simulate('click'); + break; + case 'node_attrs': + find(`${controlsSelector}.customDataAllocationOption`).simulate('click'); + break; + default: + find(`${controlsSelector}.noneDataAllocationOption`).simulate('click'); + } + }); + component.update(); + }, + setSelectedNodeAttribute: createFormSetValueAction(testBed, nodeAttrsSelector), + hasDefaultToDataNodesNotice, + hasDefaultToDataTiersNotice, + hasDefaultAllocationBehaviorNotice, + hasNoTiersAvailableNotice, + hasWillUseFallbackTierNotice, + hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), + openNodeDetailsFlyout: async () => { + await act(async () => { + find(`${phase}-viewNodeDetailsFlyoutButton`).simulate('click'); + }); + component.update(); + }, + }; +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx new file mode 100644 index 0000000000000..5ed5e6e8fcce7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/global_mocks.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +// NOTE: Import this file for its side-effects. You must import it before the code that it mocks +// is imported. Typically this means just importing above your other imports. +// See https://jestjs.io/docs/manual-mocks for more info. + +window.scrollTo = jest.fn(); + +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + + return { + ...original, + // Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions, + // which does not produce a valid component wrapper + EuiComboBox: (props: any) => ( + { + props.onChange([syntheticEvent['0']]); + }} + /> + ), + EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. + }; +}); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index 56f5815633a1d..4d63440be18c8 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -24,3 +24,10 @@ export type TestSubjects = | 'cold-freezeSwitch' | 'frozen-freezeSwitch' | string; + +export { createNodeAllocationActions } from './create_node_allocation_actions'; +export { createEnablePhaseAction } from './create_enable_phase_action'; +export { setReplicas } from './set_replicas_action'; +export { savePolicy } from './save_policy_action'; +export { createFormToggleAction } from './create_form_toggle_action'; +export { createFormSetValueAction } from './create_form_set_value_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts new file mode 100644 index 0000000000000..37ce4f84f03ba --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/save_policy_action.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act } from 'react-dom/test-utils'; +import { TestBed } from '@kbn/test/jest'; + +export const savePolicy = async (testBed: TestBed) => { + const { find, component } = testBed; + + await act(async () => { + find('savePolicyButton').simulate('click'); + }); + + component.update(); +}; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts new file mode 100644 index 0000000000000..05aec156e55bd --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TestBed } from '@kbn/test/jest'; + +import { Phases as PolicyPhases } from '../../../common/types'; +import { createFormToggleAction } from './create_form_toggle_action'; +import { createFormSetValueAction } from './create_form_set_value_action'; + +type Phases = keyof PolicyPhases; + +export const setReplicas = async (testBed: TestBed, phase: Phases, value: string) => { + const { exists } = testBed; + + if (!exists(`${phase}-selectedReplicaCount`)) { + await createFormToggleAction(testBed, `${phase}-setReplicasSwitch`)(true); + } + await createFormSetValueAction(testBed, `${phase}-selectedReplicaCount`)(value); +}; From 4e06e3bf6aa07b633ede66e83fb9fd62a6fefc4b Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 27 Apr 2021 06:28:19 -0700 Subject: [PATCH 07/13] Consume init_test_bed in edit_policy.helpers. --- .../edit_policy/edit_policy.helpers.tsx | 75 +------------------ 1 file changed, 2 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index 75a13142196f9..e3bef9a56ab6a 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -5,20 +5,11 @@ * 2.0. */ -import React from 'react'; import { act } from 'react-dom/test-utils'; - -import { registerTestBed, TestBedConfig } from '@kbn/test/jest'; - -import { licensingMock } from '../../../../licensing/public/mocks'; - -import { EditPolicy } from '../../../public/application/sections/edit_policy'; +import { TestBedConfig } from '@kbn/test/jest'; import { Phases as PolicyPhases } from '../../../common/types'; - -import { KibanaContextProvider } from '../../../public/shared_imports'; import { AppServicesContext } from '../../../public/types'; -import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; import { createEnablePhaseAction, @@ -28,72 +19,10 @@ import { setReplicas, savePolicy, } from '../helpers'; -import { TestSubjects } from '../helpers'; -import { POLICY_NAME } from './constants'; +import { initTestBed } from './init_test_bed'; type Phases = keyof PolicyPhases; - -window.scrollTo = jest.fn(); - -jest.mock('@elastic/eui', () => { - const original = jest.requireActual('@elastic/eui'); - - return { - ...original, - // Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions, - // which does not produce a valid component wrapper - EuiComboBox: (props: any) => ( - { - props.onChange([syntheticEvent['0']]); - }} - /> - ), - EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. - }; -}); - -const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { - return { - memoryRouter: { - initialEntries: [`/policies/edit/${POLICY_NAME}`], - componentRoutePath: `/policies/edit/:policyName`, - }, - defaultProps: { - getUrlForApp: () => {}, - }, - ...testBedConfigArgs, - }; -}; - -const breadcrumbService = createBreadcrumbsMock(); - -const MyComponent = ({ appServicesContext, ...rest }: any) => { - return ( - - - - ); -}; - -const initTestBed = (arg?: { - appServicesContext?: Partial; - testBedConfig?: Partial; -}) => { - const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; - return registerTestBed(MyComponent, getTestBedConfig(testBedConfigArgs))(rest); -}; - type SetupReturn = ReturnType; - export type EditPolicyTestBed = SetupReturn extends Promise ? U : SetupReturn; export const setup = async (arg?: { From 3bae91c7d432811ff93539dc9169d711f01be86a Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Tue, 27 Apr 2021 08:38:10 -0700 Subject: [PATCH 08/13] Create helpers/types file to store extracted Phase type. --- .../edit_policy/edit_policy.helpers.tsx | 19 ++++++------ .../helpers/create_enable_phase_action.ts | 6 ++-- .../helpers/create_node_allocation_actions.ts | 6 ++-- .../client_integration/helpers/index.ts | 20 +------------ .../helpers/set_replicas_action.ts | 6 ++-- .../client_integration/helpers/types.ts | 30 +++++++++++++++++++ 6 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index e3bef9a56ab6a..67a978bb08471 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -8,10 +8,10 @@ import { act } from 'react-dom/test-utils'; import { TestBedConfig } from '@kbn/test/jest'; -import { Phases as PolicyPhases } from '../../../common/types'; import { AppServicesContext } from '../../../public/types'; import { + Phase, createEnablePhaseAction, createNodeAllocationActions, createFormToggleAction, @@ -21,7 +21,6 @@ import { } from '../helpers'; import { initTestBed } from './init_test_bed'; -type Phases = keyof PolicyPhases; type SetupReturn = ReturnType; export type EditPolicyTestBed = SetupReturn extends Promise ? U : SetupReturn; @@ -87,7 +86,7 @@ export const setup = async (arg?: { component.update(); }; - const createForceMergeActions = (phase: Phases) => { + const createForceMergeActions = (phase: Phase) => { const toggleSelector = `${phase}-forceMergeSwitch`; return { forceMergeFieldExists: () => exists(toggleSelector), @@ -100,7 +99,7 @@ export const setup = async (arg?: { }; }; - const createIndexPriorityActions = (phase: Phases) => { + const createIndexPriorityActions = (phase: Phase) => { const toggleSelector = `${phase}-indexPrioritySwitch`; return { indexPriorityExists: () => exists(toggleSelector), @@ -109,7 +108,7 @@ export const setup = async (arg?: { }; }; - const createMinAgeActions = (phase: Phases) => { + const createMinAgeActions = (phase: Phase) => { return { hasMinAgeInput: () => exists(`${phase}-selectedMinimumAge`), setMinAgeValue: createFormSetValueAction(testBed, `${phase}-selectedMinimumAge`), @@ -118,7 +117,7 @@ export const setup = async (arg?: { }; }; - const createShrinkActions = (phase: Phases) => { + const createShrinkActions = (phase: Phase) => { const toggleSelector = `${phase}-shrinkSwitch`; return { shrinkExists: () => exists(toggleSelector), @@ -127,11 +126,11 @@ export const setup = async (arg?: { }; }; - const createSetFreeze = (phase: Phases) => + const createSetFreeze = (phase: Phase) => createFormToggleAction(testBed, `${phase}-freezeSwitch`); - const createFreezeExists = (phase: Phases) => () => exists(`${phase}-freezeSwitch`); + const createFreezeExists = (phase: Phase) => () => exists(`${phase}-freezeSwitch`); - const createReadonlyActions = (phase: Phases) => { + const createReadonlyActions = (phase: Phase) => { const toggleSelector = `${phase}-readonlySwitch`; return { readonlyExists: () => exists(toggleSelector), @@ -139,7 +138,7 @@ export const setup = async (arg?: { }; }; - const createSearchableSnapshotActions = (phase: Phases) => { + const createSearchableSnapshotActions = (phase: Phase) => { const fieldSelector = `searchableSnapshotField-${phase}`; const licenseCalloutSelector = `${fieldSelector}.searchableSnapshotDisabledDueToLicense`; const toggleSelector = `${fieldSelector}.searchableSnapshotToggle`; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts index 9f6642b991f92..e0988daf52192 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_enable_phase_action.ts @@ -7,11 +7,9 @@ import { TestBed } from '@kbn/test/jest'; -import { Phases as PolicyPhases } from '../../../common/types'; +import { Phase } from './types'; import { createFormToggleAction } from './create_form_toggle_action'; -type Phases = keyof PolicyPhases; - -export const createEnablePhaseAction = (testBed: TestBed, phase: Phases) => { +export const createEnablePhaseAction = (testBed: TestBed, phase: Phase) => { return createFormToggleAction(testBed, `enablePhaseSwitch-${phase}`); }; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts index 331494c47a82c..75fb71ca8938b 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts @@ -8,13 +8,11 @@ import { act } from 'react-dom/test-utils'; import { TestBed } from '@kbn/test/jest'; -import { Phases as PolicyPhases } from '../../../common/types'; +import { Phase } from './types'; import { DataTierAllocationType } from '../../../public/application/sections/edit_policy/types'; import { createFormSetValueAction } from './create_form_set_value_action'; -type Phases = keyof PolicyPhases; - -export const createNodeAllocationActions = (testBed: TestBed, phase: Phases) => { +export const createNodeAllocationActions = (testBed: TestBed, phase: Phase) => { const { component, find, exists } = testBed; const controlsSelector = `${phase}-dataTierAllocationControls`; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index 4d63440be18c8..1509d9b160559 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -5,25 +5,7 @@ * 2.0. */ -export type TestSubjects = - | 'snapshotPolicyCombobox' - | 'savePolicyButton' - | 'customPolicyCallout' - | 'noPoliciesCallout' - | 'policiesErrorCallout' - | 'rolloverSwitch' - | 'rolloverSettingsRequired' - | 'hot-selectedMaxSizeStored' - | 'hot-selectedMaxSizeStoredUnits' - | 'hot-selectedMaxDocuments' - | 'hot-selectedMaxAge' - | 'hot-selectedMaxAgeUnits' - | 'policyTablePolicyNameLink' - | 'policyTitle' - | 'createPolicyButton' - | 'cold-freezeSwitch' - | 'frozen-freezeSwitch' - | string; +export { TestSubjects, Phase } from './types'; export { createNodeAllocationActions } from './create_node_allocation_actions'; export { createEnablePhaseAction } from './create_enable_phase_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts index 05aec156e55bd..049c1a205cbd4 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/set_replicas_action.ts @@ -7,13 +7,11 @@ import { TestBed } from '@kbn/test/jest'; -import { Phases as PolicyPhases } from '../../../common/types'; +import { Phase } from './types'; import { createFormToggleAction } from './create_form_toggle_action'; import { createFormSetValueAction } from './create_form_set_value_action'; -type Phases = keyof PolicyPhases; - -export const setReplicas = async (testBed: TestBed, phase: Phases, value: string) => { +export const setReplicas = async (testBed: TestBed, phase: Phase, value: string) => { const { exists } = testBed; if (!exists(`${phase}-selectedReplicaCount`)) { diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts new file mode 100644 index 0000000000000..27e767253356d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Phases } from '../../../common/types'; + +export type Phase = keyof Phases; + +export type TestSubjects = + | 'snapshotPolicyCombobox' + | 'savePolicyButton' + | 'customPolicyCallout' + | 'noPoliciesCallout' + | 'policiesErrorCallout' + | 'rolloverSwitch' + | 'rolloverSettingsRequired' + | 'hot-selectedMaxSizeStored' + | 'hot-selectedMaxSizeStoredUnits' + | 'hot-selectedMaxDocuments' + | 'hot-selectedMaxAge' + | 'hot-selectedMaxAgeUnits' + | 'policyTablePolicyNameLink' + | 'policyTitle' + | 'createPolicyButton' + | 'cold-freezeSwitch' + | 'frozen-freezeSwitch' + | string; From d3f480ffdecefbe454eeadcfefb6b218b925db96 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 28 Apr 2021 13:18:33 -0700 Subject: [PATCH 09/13] Apply JL's feedback: rename tsx->ts filetypes, fix typo, remove unused TestSubjects type. --- .../client_integration/app/app.helpers.tsx | 3 +-- ...hase.helpers.tsx => cold_phase.helpers.ts} | 0 ...hase.helpers.tsx => warm_phase.helpers.ts} | 0 .../edit_policy/init_test_bed.tsx | 6 +----- .../client_integration/helpers/index.ts | 2 +- .../client_integration/helpers/types.ts | 20 ------------------- .../components/node_allocation.tsx | 2 +- 7 files changed, 4 insertions(+), 29 deletions(-) rename x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/{cold_phase.helpers.tsx => cold_phase.helpers.ts} (100%) rename x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/{warm_phase.helpers.tsx => warm_phase.helpers.ts} (100%) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx index 808d8ad238c3e..77b0372e24994 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.helpers.tsx @@ -12,7 +12,6 @@ import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_reac import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; import { licensingMock } from '../../../../licensing/public/mocks'; import { App } from '../../../public/application/app'; -import { TestSubjects } from '../helpers'; const breadcrumbService = createBreadcrumbsMock(); @@ -37,7 +36,7 @@ const getTestBedConfig = (initialEntries: string[]): TestBedConfig => ({ const initTestBed = (initialEntries: string[]) => registerTestBed(AppWithContext, getTestBedConfig(initialEntries))(); -export interface AppTestBed extends TestBed { +export interface AppTestBed extends TestBed { actions: { clickPolicyNameLink: () => void; clickCreatePolicyButton: () => void; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.tsx rename to x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts similarity index 100% rename from x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.tsx rename to x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.helpers.ts diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx index f54f86b8d513d..4f057e04c85d4 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/init_test_bed.tsx @@ -15,7 +15,6 @@ import { EditPolicy } from '../../../public/application/sections/edit_policy'; import { KibanaContextProvider } from '../../../public/shared_imports'; import { AppServicesContext } from '../../../public/types'; import { createBreadcrumbsMock } from '../../../public/application/services/breadcrumbs.mock'; -import { TestSubjects } from '../helpers'; import { POLICY_NAME } from './constants'; const getTestBedConfig = (testBedConfigArgs?: Partial): TestBedConfig => { @@ -52,8 +51,5 @@ export const initTestBed = (arg?: { testBedConfig?: Partial; }) => { const { testBedConfig: testBedConfigArgs, ...rest } = arg || {}; - return registerTestBed( - EditPolicyContainer, - getTestBedConfig(testBedConfigArgs) - )(rest); + return registerTestBed(EditPolicyContainer, getTestBedConfig(testBedConfigArgs))(rest); }; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts index 1509d9b160559..1388cf97d4e22 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { TestSubjects, Phase } from './types'; +export { Phase } from './types'; export { createNodeAllocationActions } from './create_node_allocation_actions'; export { createEnablePhaseAction } from './create_enable_phase_action'; diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts index 27e767253356d..644ada96a9f05 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/types.ts @@ -8,23 +8,3 @@ import { Phases } from '../../../common/types'; export type Phase = keyof Phases; - -export type TestSubjects = - | 'snapshotPolicyCombobox' - | 'savePolicyButton' - | 'customPolicyCallout' - | 'noPoliciesCallout' - | 'policiesErrorCallout' - | 'rolloverSwitch' - | 'rolloverSettingsRequired' - | 'hot-selectedMaxSizeStored' - | 'hot-selectedMaxSizeStoredUnits' - | 'hot-selectedMaxDocuments' - | 'hot-selectedMaxAge' - | 'hot-selectedMaxAgeUnits' - | 'policyTablePolicyNameLink' - | 'policyTitle' - | 'createPolicyButton' - | 'cold-freezeSwitch' - | 'frozen-freezeSwitch' - | string; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx index 109ef12a3bd50..e7b1f7420d132 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_allocation.tsx @@ -69,7 +69,7 @@ export const NodeAllocation: FunctionComponent = ({ let nodeAllocationOptions = []; // On Cloud, allocating to data tiers and allocating to data nodes is mutually exclusive. So we - // only let users select this option if they're using data nodes. Otherwise we remove it.r + // only let users select this option if they're using data nodes. Otherwise we remove it. // // On prem, users should have the freedom to choose this option, even if they're using node roles. // So we always give them this option. From d0e21f069f7b995eb4dcad7817cb6dabd5d20064 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 29 Apr 2021 19:21:30 -0700 Subject: [PATCH 10/13] Adjust copy to consistently notify the user that node attributes are missing and guide them to use role-based allocation. - Provide link to the migration docs. - Move description of default allocation behavior out of the callout title into the body. - Create no_tiers_available_using_node_attributes_notice and will_use_fallback_tier_using_node_attributes_notice components to disambiguate this branch from the node roles oriented branch. --- .../default_to_data_nodes_notice.tsx | 25 ++++++----- .../default_to_data_tiers_notice.tsx | 42 ++++++++++++------ .../components/index.ts | 4 ++ .../no_custom_attributes_messages.tsx | 37 ++++++++++++++++ .../components/no_tiers_available_notice.tsx | 1 - ...available_using_node_attributes_notice.tsx | 36 +++++++++++++++ .../node_role_to_fallback_tier_map.ts | 19 ++++++++ .../will_use_fallback_tier_notice.tsx | 29 ++---------- ...back_tier_using_node_attributes_notice.tsx | 44 +++++++++++++++++++ .../data_tier_allocation_field.tsx | 8 ++-- .../application/services/documentation.ts | 2 + .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 13 files changed, 191 insertions(+), 60 deletions(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx index dacd7416984fd..b29ddb75c18cf 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_nodes_notice.tsx @@ -10,16 +10,10 @@ import { EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { PhaseWithAllocation } from '../../../../../../../../../common/types'; - -const i18nTexts = { - title: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesLabel', { - defaultMessage: 'Store data on any available data node', - }), - body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesDescription', { - defaultMessage: - 'To allocate data to particular data nodes, use role-based allocation or configure custom node attributes in elasticsearch.yml.', - }), -}; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; export const DefaultToDataNodesNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ phase, @@ -28,10 +22,17 @@ export const DefaultToDataNodesNotice: FunctionComponent<{ phase: PhaseWithAlloc - {i18nTexts.body} +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultToDataNodesDescription', + { defaultMessage: 'Data will be allocated to any available data node.' } + )} +

+ + {nodeAllocationMigrationGuidance}
); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx index 030584c582daa..25414406e67b5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/default_to_data_tiers_notice.tsx @@ -10,22 +10,38 @@ import { EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { PhaseWithAllocation } from '../../../../../../../../../common/types'; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; const i18nTexts = { - title: { - warm: i18n.translate( - 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'Indices will be allocated to the warm tier' } + body: { + warm: ( + <> +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableDescription', + { defaultMessage: 'Data will be allocated to the warm tier.' } + )} +

+ + {nodeAllocationMigrationGuidance} + ), - cold: i18n.translate( - 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle', - { defaultMessage: 'Indices will be allocated to the cold tier' } + cold: ( + <> +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableDescription', + { defaultMessage: 'Data will be allocated to the cold tier.' } + )} +

+ + {nodeAllocationMigrationGuidance} + ), }, - body: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.defaultToDataTiersDescription', { - defaultMessage: - 'To control allocation with custom node attributes instead of the built-in node roles, configure node attributes in elasticsearch.yml.', - }), }; export const DefaultToDataTiersNotice: FunctionComponent<{ phase: PhaseWithAllocation }> = ({ @@ -35,10 +51,10 @@ export const DefaultToDataTiersNotice: FunctionComponent<{ phase: PhaseWithAlloc - {i18nTexts.body} + {i18nTexts.body[phase]} ); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts index 3f2330bed9e2a..ce5685ba98307 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/index.ts @@ -13,8 +13,12 @@ export { DataTierAllocation } from './data_tier_allocation'; export { WillUseFallbackTierNotice } from './will_use_fallback_tier_notice'; +export { WillUseFallbackTierUsingNodeAttributesNotice } from './will_use_fallback_tier_using_node_attributes_notice'; + export { NoTiersAvailableNotice } from './no_tiers_available_notice'; +export { NoTiersAvailableUsingNodeAttributesNotice } from './no_tiers_available_using_node_attributes_notice'; + export { DefaultToDataTiersNotice } from './default_to_data_tiers_notice'; export { DefaultToDataNodesNotice } from './default_to_data_nodes_notice'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx new file mode 100644 index 0000000000000..4d948ac202eb8 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_custom_attributes_messages.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { getNodeAllocationMigrationLink } from '../../../../../../../services/documentation'; + +export const noCustomAttributesTitle = i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.noCustomAttributesTitle', + { defaultMessage: 'No custom attributes defined' } +); + +export const nodeAllocationMigrationGuidance = ( + + {i18n.translate( + 'xpack.indexLifecycleMgmt.editPolicy.defaultToDataNodesDescription.migrationGuidanceMessage', + { + defaultMessage: 'use role-based allocation', + } + )} + + ), + }} + /> +); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx index f84ac017fe8a1..d4e8e98e9b2be 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_notice.tsx @@ -34,7 +34,6 @@ const i18nTexts = { interface Props { phase: PhaseWithAllocation; - isUsingNodeAttributes?: boolean; } export const NoTiersAvailableNotice: FunctionComponent = ({ phase }) => { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx new file mode 100644 index 0000000000000..1afadc0bed982 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; + +export const NoTiersAvailableUsingNodeAttributesNotice: FunctionComponent = () => { + return ( + +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.dataTier.noTiersAvailableUsingNodeAttributesDescription', + { + defaultMessage: 'There are no available nodes for data allocation.', + } + )} +

+ +

{nodeAllocationMigrationGuidance}

+
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts new file mode 100644 index 0000000000000..ad17855f307c7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/node_role_to_fallback_tier_map.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +import { DataTierRole } from '../../../../../../../../../common/types'; + +export const nodeRoleToFallbackTierMap: Partial> = { + data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { + defaultMessage: 'hot', + }), + data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { + defaultMessage: 'warm', + }), +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx index 6139280e333e8..90ac6e02ef9e2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_notice.tsx @@ -10,15 +10,7 @@ import React, { FunctionComponent } from 'react'; import { EuiCallOut } from '@elastic/eui'; import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../../common/types'; - -const nodeRoleToFallbackTierMap: Partial> = { - data_hot: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierHotLabel', { - defaultMessage: 'hot', - }), - data_warm: i18n.translate('xpack.indexLifecycleMgmt.editPolicy.dataTierWarmLabel', { - defaultMessage: 'warm', - }), -}; +import { nodeRoleToFallbackTierMap } from './node_role_to_fallback_tier_map'; const i18nTexts = { warm: { @@ -43,30 +35,15 @@ const i18nTexts = { }, }; -const customizeWithNodeAttributeDescription = i18n.translate( - 'xpack.indexLifecycleMgmt.editPolicy.customizeWithNodeAttributeDescription', - { - defaultMessage: - 'To allocate data to particular data nodes, use role-based allocation or configure custom node attributes in elasticsearch.yml.', - } -); - interface Props { phase: PhaseWithAllocation; targetNodeRole: DataTierRole; - isUsingNodeAttributes?: boolean; } -export const WillUseFallbackTierNotice: FunctionComponent = ({ - isUsingNodeAttributes, - phase, - targetNodeRole, -}) => { +export const WillUseFallbackTierNotice: FunctionComponent = ({ phase, targetNodeRole }) => { return ( -

{i18nTexts[phase].body(targetNodeRole)}

- - {isUsingNodeAttributes &&

{customizeWithNodeAttributeDescription}

} + {i18nTexts[phase].body(targetNodeRole)}
); }; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx new file mode 100644 index 0000000000000..ba2e1d25f379e --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut } from '@elastic/eui'; + +import { DataTierRole } from '../../../../../../../../../common/types'; +import { + noCustomAttributesTitle, + nodeAllocationMigrationGuidance, +} from './no_custom_attributes_messages'; +import { nodeRoleToFallbackTierMap } from './node_role_to_fallback_tier_map'; + +interface Props { + targetNodeRole: DataTierRole; +} + +export const WillUseFallbackTierUsingNodeAttributesNotice: FunctionComponent = ({ + targetNodeRole, +}) => { + return ( + +

+ {i18n.translate( + 'xpack.indexLifecycleMgmt.dataTier.willUseFallbackTierUsingNodeAttributesDescription', + { + defaultMessage: 'Data will be allocated to the {tier} tier.', + values: { tier: nodeRoleToFallbackTierMap[targetNodeRole] }, + } + )} +

+ +

{nodeAllocationMigrationGuidance}

+
+ ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 4802d38f9f2da..1f69f5622864a 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -19,7 +19,9 @@ import { DataTierAllocationType } from '../../../../types'; import { DataTierAllocation, WillUseFallbackTierNotice, + WillUseFallbackTierUsingNodeAttributesNotice, NoTiersAvailableNotice, + NoTiersAvailableUsingNodeAttributesNotice, DefaultToDataNodesNotice, DefaultToDataTiersNotice, CloudDataTierCallout, @@ -133,7 +135,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr return ( <> - + ); } @@ -146,9 +148,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr return ( <> - diff --git a/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts b/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts index bb14f1d03f31c..27e571248d66d 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/services/documentation.ts @@ -22,3 +22,5 @@ export function init(esDocBasePath: string): void { } export const createDocLink = (docPath: string): string => `${_esDocBasePath}${docPath}`; +export const getNodeAllocationMigrationLink = () => + `${_esDocBasePath}migrate-index-allocation-filters.html`; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 14b2d0c7d7201..f94be2c1ac3a1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9920,7 +9920,6 @@ "xpack.indexLifecycleMgmt.appTitle": "インデックスライフサイクルポリシー", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "ポリシーの編集", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "インデックスライフサイクル管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "コールドティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。安価なハードウェアのコールドフェーズにデータを格納します。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "インデックスを凍結", "xpack.indexLifecycleMgmt.common.dataTier.title": "データ割り当て", @@ -10206,7 +10205,6 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "ホットフェーズ", "xpack.indexLifecycleMgmt.timeline.title": "ポリシー概要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "ウォームフェーズ", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "ウォームティアに割り当てられているノードがありません", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "頻度が低い読み取り専用アクセス用に最適化されたノードにデータを移動します。", "xpack.infra.alerting.alertDropdownTitle": "アラート", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "なし (グループなし) ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2a8d2690dfc47..5733d1c7dbe8c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10051,7 +10051,6 @@ "xpack.indexLifecycleMgmt.appTitle": "索引生命周期策略", "xpack.indexLifecycleMgmt.breadcrumb.editPolicyLabel": "编辑策略", "xpack.indexLifecycleMgmt.breadcrumb.homeLabel": "索引生命周期管理", - "xpack.indexLifecycleMgmt.coldPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到冷层的节点", "xpack.indexLifecycleMgmt.coldPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。将处于冷阶段的数据存储在成本较低的硬件上。", "xpack.indexLifecycleMgmt.coldPhase.freezeIndexLabel": "冻结索引", "xpack.indexLifecycleMgmt.common.dataTier.title": "数据分配", @@ -10341,7 +10340,6 @@ "xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle": "热阶段", "xpack.indexLifecycleMgmt.timeline.title": "策略摘要", "xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle": "温阶段", - "xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotAvailableTitle": "没有分配到温层的节点", "xpack.indexLifecycleMgmt.warmPhase.dataTier.description": "将数据移到针对不太频繁的只读访问优化的节点。", "xpack.infra.alerting.alertDropdownTitle": "告警", "xpack.infra.alerting.alertFlyout.groupBy.placeholder": "无内容 (未分组) ", From 923a6c27219d6a61048e5afe42a6d864172a2344 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 29 Apr 2021 19:33:41 -0700 Subject: [PATCH 11/13] Remove redundant beforeEach from general_behavior tests. --- .../node_allocation/general_behavior.test.ts | 25 ++----------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts index 5f32b8b00c387..d45e1cfe9567b 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/general_behavior.test.ts @@ -36,26 +36,8 @@ describe(' node allocation general behavior', () => { }); }; - beforeEach(async () => { - server.respondImmediately = true; - httpRequestsMockHelpers.setLoadPolicies([]); - httpRequestsMockHelpers.setListNodes({ - nodesByRoles: { data: ['node1'] }, - nodesByAttributes: { 'attribute:true': ['node1'] }, - isUsingDeprecatedDataRoleConfig: true, - }); - httpRequestsMockHelpers.setNodesDetails('attribute:true', [ - { nodeId: 'testNodeId', stats: { name: 'testNodeName', host: 'testHost' } }, - ]); - - await setup(); - - const { component } = testBed; - component.update(); - }); - describe('data allocation', () => { - beforeEach(async () => { + test('setting node_attr based allocation, but not selecting node attribute', async () => { httpRequestsMockHelpers.setLoadPolicies([POLICY_WITH_MIGRATE_OFF]); httpRequestsMockHelpers.setListNodes({ nodesByRoles: {}, @@ -66,12 +48,9 @@ describe(' node allocation general behavior', () => { await setup(); - const { component } = testBed; + const { component, actions } = testBed; component.update(); - }); - test('setting node_attr based allocation, but not selecting node attribute', async () => { - const { actions } = testBed; await actions.setDataAllocation('node_attrs'); await actions.savePolicy(); const latestRequest = server.requests[server.requests.length - 1]; From fd44a885693fcbce3b0970399766ca937bdf7006 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Thu, 29 Apr 2021 20:23:00 -0700 Subject: [PATCH 12/13] Update tests. - Refactor tests to make the [using node attributes, using node roles] branch and the various conditions within easier to contrast and compare. - Add coverage for NoTiersAvailableUsingNodeAttributesNotice scenario. - Add some helpers for improved maintainability and readability. --- .../node_allocation/cold_phase.test.ts | 249 +++++++++-------- .../node_allocation/warm_phase.test.ts | 252 ++++++++++-------- .../helpers/create_node_allocation_actions.ts | 11 + .../data_tier_allocation_field.tsx | 2 +- 4 files changed, 279 insertions(+), 235 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts index 30b229834dedd..f833c8e316117 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/cold_phase.test.ts @@ -46,166 +46,181 @@ describe(' node allocation in the cold phase', () => { component.update(); }); - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { + test(`doesn't offer allocation guidance when node with deprecated "data" role exists`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, + nodesByRoles: { data: ['test'] }, isUsingDeprecatedDataRoleConfig: false, }); - await setup(); const { actions, component } = testBed; component.update(); await actions.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeFalsy(); }); - test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the cold phase', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_warm: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); + describe('when using node attributes', () => { + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; - await setup(); - const { actions, component } = testBed; - - component.update(); - await actions.enable(true); + const { actions } = testBed; + await actions.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); + expect(actions.isAllocationLoading()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: true, + // No notices will be shown. + expect(actions.hasDefaultToDataTiersNotice()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); + expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); }); - await setup(); - const { actions, component } = testBed; - - component.update(); - await actions.enable(true); + describe('and some are defined', () => { + test('shows the node attributes input', async () => { + const { actions } = testBed; + await actions.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.enable(true); + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions } = testBed; - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.getNodeAttributesSelectOptions().length).toBe(2); - }); + await actions.enable(true); - test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); - await actions.enable(true); + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + }); - expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.getNodeAttributesSelectOptions().length).toBe(2); - await actions.setSelectedNodeAttribute('attribute:true'); + describe('and none are defined', () => { + const commonSetupAndBaselineAssertions = async () => { + await setup(); + const { actions, component } = testBed; + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }; + + test('and data tiers are available, shows DefaultToDataTiersNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + }); - await actions.openNodeDetailsFlyout(); - expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); - }); + test('and data tiers are available, but not for the cold phase, shows WillUseFallbackTierUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_warm: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); - test('offers allocation guidance when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, - }); + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeTruthy(); + }); - await setup(); - const { actions, component } = testBed; + test('when data nodes are in use, shows DefaultToDataNodesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); - component.update(); - await actions.enable(true); + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + }); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); + test('when no data tier node roles are defined, shows NoTiersAvailableUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + // data_content is a node role so they're technically in use, but it's not a data tier node role. + nodesByRoles: { data_content: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeTruthy(); + }); + }); }); - [ - { - nodesByRoles: { data_hot: ['test'] }, - fallbackTier: 'hot', - }, - { - nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, - fallbackTier: 'warm', - }, - ].forEach(({ nodesByRoles, fallbackTier }) => { - test(`in the cold phase, offers allocation guidance when allocation will fallback to the ${fallbackTier} tier`, async () => { + describe('when using node roles', () => { + test('when no node roles are defined, shows NoTiersAvailableNotice', async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, - nodesByRoles, + nodesByRoles: {}, isUsingDeprecatedDataRoleConfig: false, }); await setup(); - const { actions, component, find } = testBed; + const { actions, component } = testBed; component.update(); await actions.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(find('willUseFallbackTierNotice').text()).toContain( - `No nodes assigned to the cold tierIf no cold nodes are available, data is stored in the ${fallbackTier} tier.` - ); + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); }); - }); - test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, + [ + { + nodesByRoles: { data_hot: ['test'] }, + fallbackTier: 'hot', + }, + { + nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + fallbackTier: 'warm', + }, + ].forEach(({ nodesByRoles, fallbackTier }) => { + test(`when allocation will fallback to the ${fallbackTier} tier, shows WillUseFallbackTierNotice and defines the fallback tier`, async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles, + isUsingDeprecatedDataRoleConfig: false, + }); + + await setup(); + const { actions, component } = testBed; + + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.getWillUseFallbackTierNoticeText()).toContain( + `No nodes assigned to the cold tierIf no cold nodes are available, data is stored in the ${fallbackTier} tier.` + ); + }); }); - await setup(); - const { actions, component } = testBed; - - component.update(); - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasWillUseFallbackTierNotice()).toBeFalsy(); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts index a8ab57c21df61..0ab18c6e05736 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/features/node_allocation/warm_phase.test.ts @@ -46,151 +46,169 @@ describe(' node allocation in the warm phase', () => { component.update(); }); - test('shows spinner for node attributes input when loading', async () => { - server.respondImmediately = false; - - const { actions, component } = testBed; - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeTruthy(); - expect(actions.hasDataTierAllocationControls()).toBeTruthy(); - - expect(component.find('.euiCallOut--warning').exists()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA TIER allocation guidance when no attributes exist and data tiers are available', async () => { + test(`doesn't offer allocation guidance when node with deprecated "data" role exists`, async () => { httpRequestsMockHelpers.setListNodes({ nodesByAttributes: {}, - nodesByRoles: { data: ['node1'] }, + nodesByRoles: { data: ['test'] }, isUsingDeprecatedDataRoleConfig: false, }); - await setup(); const { actions, component } = testBed; component.update(); await actions.enable(true); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); }); - test('offers DATA TIER FALLBACK allocation guidance when no attributes exist and data tiers are available, but not for the warm phase', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_hot: ['node1'] }, - isUsingDeprecatedDataRoleConfig: false, - }); + describe('when using node attributes', () => { + test('shows spinner for node attributes input when loading', async () => { + server.respondImmediately = false; - await setup(); - const { actions, component } = testBed; + const { actions } = testBed; + await actions.enable(true); - component.update(); - await actions.enable(true); + expect(actions.isAllocationLoading()).toBeTruthy(); + expect(actions.hasDataTierAllocationControls()).toBeTruthy(); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('offers DATA NODE allocation guidance when no attributes exist and data nodes are in use', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: true, + // No notices will be shown. + expect(actions.hasDefaultToDataTiersNotice()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeFalsy(); + expect(actions.hasDefaultToDataNodesNotice()).toBeFalsy(); + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeFalsy(); }); - await setup(); - const { actions, component } = testBed; - - component.update(); - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); - expect(actions.hasNodeAttributesSelect()).toBeFalsy(); - }); - - test('shows node attributes input when attributes exist', async () => { - const { actions, component } = testBed; - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeTruthy(); - expect(actions.getNodeAttributesSelectOptions().length).toBe(2); - }); - - test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { - const { actions, component } = testBed; - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - await actions.setDataAllocation('node_attrs'); - expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); - expect(actions.hasNodeAttributesSelect()).toBeTruthy(); - - expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); - expect(actions.getNodeAttributesSelectOptions().length).toBe(2); - await actions.setSelectedNodeAttribute('attribute:true'); - - await actions.openNodeDetailsFlyout(); - expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); - }); + describe('and some are defined', () => { + test('shows the node attributes input', async () => { + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + }); + + test('shows view node attributes link when an attribute is selected and shows flyout when clicked', async () => { + const { actions } = testBed; + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); + expect(actions.hasNodeAttributesSelect()).toBeTruthy(); + + expect(actions.hasNodeDetailsFlyout()).toBeFalsy(); + expect(actions.getNodeAttributesSelectOptions().length).toBe(2); + await actions.setSelectedNodeAttribute('attribute:true'); + + await actions.openNodeDetailsFlyout(); + expect(actions.hasNodeDetailsFlyout()).toBeTruthy(); + }); + }); - test('offers allocation guidance when no node roles are found', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: {}, - isUsingDeprecatedDataRoleConfig: false, + describe('and none are defined', () => { + const commonSetupAndBaselineAssertions = async () => { + await setup(); + const { actions, component } = testBed; + component.update(); + await actions.enable(true); + + expect(actions.isAllocationLoading()).toBeFalsy(); + await actions.setDataAllocation('node_attrs'); + expect(actions.hasNodeAttributesSelect()).toBeFalsy(); + }; + + test('and data tiers are available, shows DefaultToDataTiersNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataTiersNotice()).toBeTruthy(); + }); + + test('and data tiers are available, but not for the warm phase, shows WillUseFallbackTierUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasWillUseFallbackTierUsingNodeAttributesNotice()).toBeTruthy(); + }); + + test('when data nodes are in use, shows DefaultToDataNodesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: true, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasDefaultToDataNodesNotice()).toBeTruthy(); + }); + + test('when no data tier node roles are defined, shows NoTiersAvailableUsingNodeAttributesNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + // data_content is a node role so they're technically in use, but it's not a data tier node role. + nodesByRoles: { data_content: ['node1'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + + await commonSetupAndBaselineAssertions(); + const { actions } = testBed; + expect(actions.hasNoTiersAvailableUsingNodeAttributesNotice()).toBeTruthy(); + }); }); + }); - await setup(); - const { actions, component } = testBed; + describe('when using node roles', () => { + test('when no node roles are defined, shows NoTiersAvailableNotice', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: {}, + isUsingDeprecatedDataRoleConfig: false, + }); - component.update(); - await actions.enable(true); + await setup(); + const { actions, component } = testBed; - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); - }); + component.update(); + await actions.enable(true); - test('in the warm phase, offers allocation guidance when allocation will fallback to the hot tier', async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasNoTiersAvailableNotice()).toBeTruthy(); }); - await setup(); - const { actions, component } = testBed; + test('when allocation will fallback to the hot tier, shows WillUseFallbackTierNotice and defines the fallback tier', async () => { + httpRequestsMockHelpers.setListNodes({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + isUsingDeprecatedDataRoleConfig: false, + }); - component.update(); - await actions.enable(true); + await setup(); + const { actions, component } = testBed; - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); - }); + component.update(); + await actions.enable(true); - test(`doesn't offer allocation guidance when node with "data" role exists`, async () => { - httpRequestsMockHelpers.setListNodes({ - nodesByAttributes: {}, - nodesByRoles: { data: ['test'] }, - isUsingDeprecatedDataRoleConfig: false, + expect(actions.isAllocationLoading()).toBeFalsy(); + expect(actions.hasWillUseFallbackTierNotice()).toBeTruthy(); + expect(actions.getWillUseFallbackTierNoticeText()).toContain( + `No nodes assigned to the warm tierIf no warm nodes are available, data is stored in the hot tier.` + ); }); - await setup(); - const { actions, component } = testBed; - - component.update(); - await actions.enable(true); - - expect(component.find('.euiLoadingSpinner').exists()).toBeFalsy(); - expect(actions.hasDefaultAllocationBehaviorNotice()).toBeFalsy(); }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts index 75fb71ca8938b..416a2afa54d54 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/helpers/create_node_allocation_actions.ts @@ -26,12 +26,19 @@ export const createNodeAllocationActions = (testBed: TestBed, phase: Phase) => { component.update(); }; + const isAllocationLoading = () => find(`${phase}-phase.allocationLoadingSpinner`).exists(); const hasDefaultToDataNodesNotice = () => exists(`${phase}-phase.defaultToDataNodesNotice`); const hasDefaultToDataTiersNotice = () => exists(`${phase}-phase.defaultToDataTiersNotice`); const hasDefaultAllocationBehaviorNotice = () => hasDefaultToDataNodesNotice() && hasDefaultToDataTiersNotice(); const hasNoTiersAvailableNotice = () => exists(`${phase}-phase.noTiersAvailableNotice`); + const hasNoTiersAvailableUsingNodeAttributesNotice = () => + exists(`${phase}-phase.noTiersAvailableUsingNodeAttributesNotice`); const hasWillUseFallbackTierNotice = () => exists(`${phase}-phase.willUseFallbackTierNotice`); + const hasWillUseFallbackTierUsingNodeAttributesNotice = () => + exists(`${phase}-phase.willUseFallbackTierUsingNodeAttributesNotice`); + const getWillUseFallbackTierNoticeText = () => + find(`${phase}-phase.willUseFallbackTierNotice`).text(); return { hasDataTierAllocationControls: () => exists(controlsSelector), @@ -56,11 +63,15 @@ export const createNodeAllocationActions = (testBed: TestBed, phase: Phase) => { component.update(); }, setSelectedNodeAttribute: createFormSetValueAction(testBed, nodeAttrsSelector), + isAllocationLoading, hasDefaultToDataNodesNotice, hasDefaultToDataTiersNotice, hasDefaultAllocationBehaviorNotice, hasNoTiersAvailableNotice, + hasNoTiersAvailableUsingNodeAttributesNotice, hasWillUseFallbackTierNotice, + hasWillUseFallbackTierUsingNodeAttributesNotice, + getWillUseFallbackTierNoticeText, hasNodeDetailsFlyout: () => exists(`${phase}-viewNodeDetailsFlyoutButton`), openNodeDetailsFlyout: async () => { await act(async () => { diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index 1f69f5622864a..dbee88a3fea4f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -194,7 +194,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr {isLoading ? ( <> - + ) : ( error && ( From ffa283e3b5d8fa7964997d72533ae5b5d6651220 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Fri, 30 Apr 2021 19:16:57 -0700 Subject: [PATCH 13/13] Final copy polish. --- .../no_tiers_available_using_node_attributes_notice.tsx | 2 +- ...ll_use_fallback_tier_using_node_attributes_notice.tsx | 9 ++++++--- .../data_tier_allocation_field.tsx | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx index 1afadc0bed982..02dbd1baff25f 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/no_tiers_available_using_node_attributes_notice.tsx @@ -25,7 +25,7 @@ export const NoTiersAvailableUsingNodeAttributesNotice: FunctionComponent = () = {i18n.translate( 'xpack.indexLifecycleMgmt.dataTier.noTiersAvailableUsingNodeAttributesDescription', { - defaultMessage: 'There are no available nodes for data allocation.', + defaultMessage: 'Unable to allocate data: no available data nodes.', } )}

diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx index ba2e1d25f379e..199f1001f4276 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/will_use_fallback_tier_using_node_attributes_notice.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import React, { FunctionComponent } from 'react'; import { EuiCallOut } from '@elastic/eui'; -import { DataTierRole } from '../../../../../../../../../common/types'; +import { PhaseWithAllocation, DataTierRole } from '../../../../../../../../../common/types'; import { noCustomAttributesTitle, nodeAllocationMigrationGuidance, @@ -17,10 +17,12 @@ import { import { nodeRoleToFallbackTierMap } from './node_role_to_fallback_tier_map'; interface Props { + phase: PhaseWithAllocation; targetNodeRole: DataTierRole; } export const WillUseFallbackTierUsingNodeAttributesNotice: FunctionComponent = ({ + phase, targetNodeRole, }) => { return ( @@ -32,8 +34,9 @@ export const WillUseFallbackTierUsingNodeAttributesNotice: FunctionComponent diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx index dbee88a3fea4f..c45e172868938 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/data_tier_allocation_field.tsx @@ -149,6 +149,7 @@ export const DataTierAllocationField: FunctionComponent = ({ phase, descr <>