diff --git a/x-pack/plugins/index_lifecycle_management/README.md b/x-pack/plugins/index_lifecycle_management/README.md index 3b72ac85810c6..28b2a4637da89 100644 --- a/x-pack/plugins/index_lifecycle_management/README.md +++ b/x-pack/plugins/index_lifecycle_management/README.md @@ -1,6 +1,8 @@ # Index Lifecycle Management -## Quick steps for testing ILM in Index Management +## Testing + +### Quick steps for testing ILM in Index Management You can test that the `Frozen` badge, phase filtering, and lifecycle information is surfaced in Index Management by running this series of requests in Console: @@ -92,4 +94,26 @@ After about a minute, there should be an error on this index. When you click the ILM information in the detail panel as well as an error. You can dismiss the error by clicking `Manage > Retry lifecycle step`. -![image](https://user-images.githubusercontent.com/1238659/78087984-a6811000-7377-11ea-880e-1a7b182c14f1.png) \ No newline at end of file +![image](https://user-images.githubusercontent.com/1238659/78087984-a6811000-7377-11ea-880e-1a7b182c14f1.png) + +### Data tier notifications + +When creating or editing an ILM policy the UI should notify users that under certain conditions their data will not be +moved to a tier corresponding to a phase. For instance, when a cluster only has hot-tier nodes. We test the UI +with this cluster state by starting an ES node with the `data_hot` role. Using this command: + +```bash +yarn es snapshot --license=trial -E node.roles=data_hot,master,data_content +``` + +This will create a cluster where we have a single node that belongs to the hot-tier. In the data allocation section of +both the warm and cold phase you should see notice like the following: + +![image](https://user-images.githubusercontent.com/8155004/94132944-4b306600-fe60-11ea-9c3d-02229e3055b8.png) + +Default configuration for a node is that it belongs to all tiers, in which case you should not see this notice. Test +this by running: + +```bash +yarn es snapshot --license=trial +``` \ No newline at end of file diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index f195228775772..dfbe19ba21a94 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -503,6 +503,30 @@ describe('edit policy', () => { expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); expect(findTestSubject(rendered, 'defaultAllocationWarning').exists()).toBeTruthy(); }); + test('should show default allocation notice when hot tier exists, but not warm tier', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_cold: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeTruthy(); + }); + test('should not show default allocation notice when node with "data" role exists', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'warm'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + }); }); describe('cold phase', () => { beforeEach(() => { @@ -610,6 +634,30 @@ describe('edit policy', () => { expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); expect(findTestSubject(rendered, 'defaultAllocationWarning').exists()).toBeTruthy(); }); + test('should show default allocation notice when warm or hot tiers exists, but not cold tier', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data_hot: ['test'], data_warm: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeTruthy(); + }); + test('should not show default allocation notice when node with "data" role exists', async () => { + http.setupNodeListResponse({ + nodesByAttributes: {}, + nodesByRoles: { data: ['test'] }, + }); + const rendered = mountWithIntl(component); + noRollover(rendered); + setPolicyName(rendered, 'mypolicy'); + await activatePhase(rendered, 'cold'); + expect(rendered.find('.euiLoadingSpinner').exists()).toBeFalsy(); + expect(findTestSubject(rendered, 'defaultAllocationNotice').exists()).toBeFalsy(); + }); }); describe('delete phase', () => { test('should allow 0 for phase timing', async () => { diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts new file mode 100644 index 0000000000000..8a1acf72949e6 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/common/constants/data_tiers.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// Order of node roles matters here, the warm phase prefers allocating data +// to the data_warm role. +import { NodeDataRole, PhaseWithAllocation } from '../types'; + +const WARM_PHASE_NODE_PREFERENCE: NodeDataRole[] = ['data_warm', 'data_hot']; + +const COLD_PHASE_NODE_PREFERENCE: NodeDataRole[] = ['data_cold', 'data_warm', 'data_hot']; + +export const phaseToNodePreferenceMap: Record = Object.freeze({ + warm: WARM_PHASE_NODE_PREFERENCE, + cold: COLD_PHASE_NODE_PREFERENCE, +}); diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts index 5c89b917163d8..522dc6d82a4e9 100644 --- a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts @@ -7,6 +7,8 @@ import { i18n } from '@kbn/i18n'; import { LicenseType } from '../../../licensing/common/types'; +export { phaseToNodePreferenceMap } from './data_tiers'; + const basicLicense: LicenseType = 'basic'; export const PLUGIN = { diff --git a/x-pack/plugins/index_lifecycle_management/common/types/api.ts b/x-pack/plugins/index_lifecycle_management/common/types/api.ts index 16b8fbd127ab6..fcdbdf2c9cc90 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/api.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/api.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export type NodeDataRole = 'data' | 'data_hot' | 'data_warm' | 'data_cold' | 'data_frozen'; +import { NodeDataRoleWithCatchAll } from '.'; export interface ListNodesRouteResponse { nodesByAttributes: { [attributePair: string]: string[] }; - nodesByRoles: { [role in NodeDataRole]?: string[] }; + nodesByRoles: { [role in NodeDataRoleWithCatchAll]?: string[] }; } diff --git a/x-pack/plugins/index_lifecycle_management/common/types/index.ts b/x-pack/plugins/index_lifecycle_management/common/types/index.ts index a23dc647f1f65..1f41370e48f18 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/index.ts @@ -7,3 +7,10 @@ export * from './api'; export * from './policies'; + +/** + * These roles reflect how nodes are stratified into different data tiers. The "data" role + * is a catch-all that can be used to store data in any phase. + */ +export type NodeDataRole = 'data_hot' | 'data_warm' | 'data_cold'; +export type NodeDataRoleWithCatchAll = 'data' | NodeDataRole; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts deleted file mode 100644 index 2ef0fb145551f..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/check_phase_compatibility.ts +++ /dev/null @@ -1,36 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - NodeDataRole, - ListNodesRouteResponse, - PhaseWithAllocation, -} from '../../../../common/types'; - -/** - * Given a phase and current node roles, determine whether the phase - * can use default data tier allocation. - * - * This can only be checked for phases that have an allocate action. - */ -export const isPhaseDefaultDataAllocationCompatible = ( - phase: PhaseWithAllocation, - nodesByRoles: ListNodesRouteResponse['nodesByRoles'] -): boolean => { - // The 'data' role covers all node roles, so if we have at least one node with the data role - // we can use default allocation. - if (nodesByRoles.data?.length) { - return true; - } - - // Otherwise we need to check whether a node role for the specific phase exists - if (nodesByRoles[`data_${phase}` as NodeDataRole]?.length) { - return true; - } - - // Otherwise default allocation has nowhere to allocate new shards to in this phase. - return false; -}; 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 new file mode 100644 index 0000000000000..6daae57330886 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/get_available_node_roles_for_phase.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + NodeDataRole, + ListNodesRouteResponse, + PhaseWithAllocation, +} from '../../../../common/types'; + +import { phaseToNodePreferenceMap } from '../../../../common/constants'; + +export type AllocationNodeRole = NodeDataRole | '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 + * tier nodes, we would expect "data_warm". + * + * If no nodes can be identified for allocation (very special case) then + * we return "none". + */ +export const getAvailableNodeRoleForPhase = ( + phase: PhaseWithAllocation, + nodesByRoles: ListNodesRouteResponse['nodesByRoles'] +): AllocationNodeRole => { + const preferredNodeRoles = phaseToNodePreferenceMap[phase]; + + // The 'data' role covers all node roles, so if we have at least one node with the data role + // we can allocate to our first preference. + if (nodesByRoles.data?.length) { + return preferredNodeRoles[0]; + } + + return preferredNodeRoles.find((role) => Boolean(nodesByRoles[role]?.length)) ?? 'none'; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts index 67a512cefe00c..87f2cbc08ecc0 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/index.ts @@ -6,4 +6,4 @@ export * from './determine_allocation_type'; -export * from './check_phase_compatibility'; +export * from './get_available_node_roles_for_phase'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.ts b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.ts new file mode 100644 index 0000000000000..872efa740b131 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/lib/data_tiers/is_node_role_first_preference.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { NodeDataRole, PhaseWithAllocation } from '../../../../common/types'; +import { phaseToNodePreferenceMap } from '../../../../common/constants'; + +export const isNodeRoleFirstPreference = (phase: PhaseWithAllocation, nodeRole: NodeDataRole) => { + return phaseToNodePreferenceMap[phase][0] === nodeRole; +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx new file mode 100644 index 0000000000000..8faa9bb2972c2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_notice.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent } from 'react'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; + +import { PhaseWithAllocation, NodeDataRole } from '../../../../../../common/types'; + +import { AllocationNodeRole } from '../../../../lib'; + +const i18nTextsNodeRoleToDataTier: Record = { + 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', + }), +}; + +const i18nTexts = { + notice: { + warm: { + title: i18n.translate( + 'xpack.indexLifecycleMgmt.warmPhase.dataTier.defaultAllocationNotice.warm.title', + { defaultMessage: 'No nodes assigned to the warm tier' } + ), + body: (nodeRole: NodeDataRole) => + 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: NodeDataRole) => + 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] }, + }), + }, + }, + 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: AllocationNodeRole; +} + +export const DefaultAllocationNotice: FunctionComponent = ({ phase, targetNodeRole }) => { + const content = + targetNodeRole === 'none' ? ( + + {i18nTexts.warning[phase].body} + + ) : ( + + {i18nTexts.notice[phase].body(targetNodeRole)} + + ); + + return ( + <> + + {content} + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx deleted file mode 100644 index 5aba411b6fe53..0000000000000 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/default_allocation_warning.tsx +++ /dev/null @@ -1,59 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import React, { FunctionComponent } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; - -import { PhaseWithAllocation } from '../../../../../../common/types'; - -const i18nTexts = { - 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 tier to use role-based allocation. The policy will fail to complete allocation if there are no warm 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 tier to use role-based allocation. The policy will fail to complete allocation if there are no cold nodes.', - } - ), - }, -}; - -interface Props { - phase: PhaseWithAllocation; -} - -export const DefaultAllocationWarning: FunctionComponent = ({ phase }) => { - return ( - <> - - - {i18nTexts[phase].body} - - - ); -}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts index 26464a75ae14c..dcbdf960fd380 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/data_tier_allocation/index.ts @@ -8,5 +8,5 @@ export { NodesDataProvider } from './node_data_provider'; export { NodeAllocation } from './node_allocation'; export { NodeAttrsDetails } from './node_attrs_details'; export { DataTierAllocation } from './data_tier_allocation'; -export { DefaultAllocationWarning } from './default_allocation_warning'; +export { DefaultAllocationNotice } from './default_allocation_notice'; export { NoNodeAttributesWarning } from './no_node_attributes_warning'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts index 2428cade0898e..c39545112ee52 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts @@ -18,7 +18,7 @@ export { NodeAllocation, NodeAttrsDetails, NodesDataProvider, - DefaultAllocationWarning, + DefaultAllocationNotice, } from './data_tier_allocation'; export { DescribedFormField } from './described_form_field'; export { Forcemerge } from './forcemerge'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx index 241a98fffa6df..b9b1b8b663ec8 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/cold_phase.tsx @@ -33,7 +33,7 @@ const i18nTexts = { dataTierAllocation: { description: i18n.translate('xpack.indexLifecycleMgmt.coldPhase.dataTier.description', { defaultMessage: - 'Move data to data nodes optimized for less frequent, read-only access. Store cold data on less-expensive hardware.', + 'Move data to nodes optimized for less frequent, read-only access. Store data in the cold phase on less-expensive hardware.', }), }, }; @@ -192,8 +192,7 @@ export const ColdPhase: FunctionComponent = ({ {' '} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx index 6475e5286a778..623d443a1db01 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/shared/data_tier_allocation_field.tsx @@ -9,15 +9,16 @@ import { i18n } from '@kbn/i18n'; import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; import { PhaseWithAllocationAction, PhaseWithAllocation } from '../../../../../../common/types'; +import { PhaseValidationErrors } from '../../../../services/policies/policy_validation'; +import { getAvailableNodeRoleForPhase } from '../../../../lib/data_tiers'; +import { isNodeRoleFirstPreference } from '../../../../lib/data_tiers/is_node_role_first_preference'; import { DataTierAllocation, - DefaultAllocationWarning, + DefaultAllocationNotice, NoNodeAttributesWarning, NodesDataProvider, } from '../../components/data_tier_allocation'; -import { PhaseValidationErrors } from '../../../../services/policies/policy_validation'; -import { isPhaseDefaultDataAllocationCompatible } from '../../../../lib/data_tiers'; const i18nTexts = { title: i18n.translate('xpack.indexLifecycleMgmt.common.dataTier.title', { @@ -48,9 +49,34 @@ export const DataTierAllocationField: FunctionComponent = ({ return ( {(nodesData) => { - const isCompatible = isPhaseDefaultDataAllocationCompatible(phase, nodesData.nodesByRoles); const hasNodeAttrs = Boolean(Object.keys(nodesData.nodesByAttributes ?? {}).length); + const renderDefaultAllocationNotice = () => { + if (phaseData.dataTierAllocationType !== 'default') { + return null; + } + + const allocationNodeRole = getAvailableNodeRoleForPhase(phase, nodesData.nodesByRoles); + if ( + allocationNodeRole !== 'none' && + isNodeRoleFirstPreference(phase, allocationNodeRole) + ) { + return null; + } + + return ; + }; + + const renderNodeAttributesWarning = () => { + if (phaseData.dataTierAllocationType !== 'custom') { + return null; + } + if (hasNodeAttrs) { + return null; + } + return ; + }; + return ( {i18nTexts.title}} @@ -70,14 +96,8 @@ export const DataTierAllocationField: FunctionComponent = ({ /> {/* Data tier related warnings */} - - {phaseData.dataTierAllocationType === 'default' && !isCompatible && ( - - )} - - {phaseData.dataTierAllocationType === 'custom' && !hasNodeAttrs && ( - - )} + {renderDefaultAllocationNotice()} + {renderNodeAttributesWarning()} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx index 16a740b1171c9..b837eed1256c5 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/phases/warm_phase.tsx @@ -45,8 +45,7 @@ const i18nTexts = { ), dataTierAllocation: { description: i18n.translate('xpack.indexLifecycleMgmt.warmPhase.dataTier.description', { - defaultMessage: - 'Move warm data to nodes optimized for read-only access. Store warm data on less-expensive hardware.', + defaultMessage: 'Move data to nodes optimized for less-frequent, read-only access.', }), }, };