[8.18] [UA] Special handling of ES transforms (#211418) (#211562)

# Backport

This will backport the following commits from `8.x` to `8.18`:
- [[UA] Special handling of ES transforms
(#211418)](https://github.com/elastic/kibana/pull/211418)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Jean-Louis
Leysens","email":"jeanlouis.leysens@elastic.co"},"sourceCommit":{"committedDate":"2025-02-17T15:25:06Z","message":"[UA]
Special handling of ES transforms (#211418)\n\nClose
https://github.com/elastic/kibana-team/issues/1293\r\n\r\nRelated
https://github.com/elastic/elasticsearch/pull/122192\r\n\r\n<img
width=\"503\" alt=\"Screenshot 2025-02-17 at 14 38
53\"\r\nsrc=\"https://github.com/user-attachments/assets/06131482-8547-4296-bd7e-e1c7c879fb89\"\r\n/>","sha":"c6e0eeb43d4b6ca12adea736c8513427ed68d851","branchLabelMapping":{"^v8.16.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","Feature:Upgrade
Assistant","backport:version","v8.18.0","v8.19.0"],"title":"[UA] Special
handling of ES
transforms","number":211418,"url":"https://github.com/elastic/kibana/pull/211418","mergeCommit":{"message":"[UA]
Special handling of ES transforms (#211418)\n\nClose
https://github.com/elastic/kibana-team/issues/1293\r\n\r\nRelated
https://github.com/elastic/elasticsearch/pull/122192\r\n\r\n<img
width=\"503\" alt=\"Screenshot 2025-02-17 at 14 38
53\"\r\nsrc=\"https://github.com/user-attachments/assets/06131482-8547-4296-bd7e-e1c7c879fb89\"\r\n/>","sha":"c6e0eeb43d4b6ca12adea736c8513427ed68d851"}},"sourceBranch":"8.x","suggestedTargetBranches":["8.18"],"targetPullRequestStates":[{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->
This commit is contained in:
Jean-Louis Leysens 2025-02-19 10:40:15 +01:00 committed by GitHub
parent 8697f4e61e
commit aa8c9f16b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 260 additions and 18 deletions

View file

@ -203,6 +203,11 @@ export interface ReindexAction {
* In future this could be an array of blockers.
*/
blockerForReindexing?: 'index-closed'; // 'index-closed' can be handled automatically, but requires more resources, user should be warned
/**
* The transform IDs that are currently targeting this index
*/
transformIds?: string[];
}
export interface UnfreezeAction {

View file

@ -154,6 +154,7 @@ export const IndexFlyout: React.FunctionComponent<IndexFlyoutProps> = ({
startReadonly={() => {
setFlyoutStep('confirmReadonly');
}}
deprecation={deprecation}
updateIndexState={updateIndexState}
reindexState={reindexState}
/>
@ -214,6 +215,7 @@ export const IndexFlyout: React.FunctionComponent<IndexFlyoutProps> = ({
}, [
flyoutStep,
correctiveAction?.type,
deprecation,
closeFlyout,
updateIndexState,
reindexState,

View file

@ -0,0 +1,79 @@
/*
* 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 { EuiCallOut, EuiLink, EuiText, EuiSpacer } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import type { EnrichedDeprecationInfo } from '../../../../../../../../../common/types';
import { useAppContext } from '../../../../../../../app_context';
interface Props {
deprecation: EnrichedDeprecationInfo;
}
/**
* We get copy directly from ES. This contains information that applies to indices
* that are read-only or not.
*/
export const ESTransformsTargetGuidance = ({ deprecation }: Props) => {
const {
services: {
core: { http },
},
} = useAppContext();
return (
<>
<EuiCallOut
title={i18n.translate(
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.calloutTitle',
{ defaultMessage: 'Transforms detected' }
)}
data-test-subj="esTransformsGuidance"
color="warning"
>
{deprecation.details}
</EuiCallOut>
<EuiSpacer size="s" />
<EuiText size="m">
<p>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.description1"
defaultMessage="The reindex operation will copy all of the existing documents into a new index and remove the old one. During the reindex operation your data will be in a read-only state and transforms writing to this index will be paused."
/>
</p>
<p>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.description2"
defaultMessage="Depending on size and resources, reindexing may take an extended time. For indices with more than 10GB of data or to avoid transform downtime refer to the {migrationGuideLink} or {transformsLink} to manage transforms writing to this index."
values={{
migrationGuideLink: (
<EuiLink target="_blank" href={deprecation.url}>
{i18n.translate(
'xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.migrationGuideLink',
{ defaultMessage: 'migration guide' }
)}
</EuiLink>
),
transformsLink: (
<EuiLink
target="_blank"
href={`${http.basePath.prepend('/app/management/data/transform')}`}
>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.esTransform.transfromsLink"
defaultMessage="go to transforms"
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
</>
);
};

View file

@ -12,6 +12,7 @@ import type { ReindexState } from '../../../use_reindex';
import type { UpdateIndexState } from '../../../use_update_index';
import { LoadingState } from '../../../../../../types';
import { cloneDeep } from 'lodash';
import { EnrichedDeprecationInfo } from '../../../../../../../../../common/types';
jest.mock('../../../../../../../app_context', () => {
const { docLinksServiceMock } = jest.requireActual('@kbn/core-doc-links-browser-mocks');
@ -38,6 +39,13 @@ jest.mock('../../../../../../../app_context', () => {
});
describe('ReindexDetailsFlyoutStep', () => {
const deprecation: EnrichedDeprecationInfo = {
isCritical: true,
message: 'foo',
resolveDuringUpgrade: false,
type: 'index_settings',
url: 'https://te.st',
};
const defaultReindexState: ReindexState = {
loadingState: LoadingState.Success,
meta: {
@ -65,6 +73,7 @@ describe('ReindexDetailsFlyoutStep', () => {
startReadonly={jest.fn()}
reindexState={defaultReindexState}
updateIndexState={defaultUpdateIndexState}
deprecation={deprecation}
/>
);
@ -206,6 +215,94 @@ describe('ReindexDetailsFlyoutStep', () => {
`);
});
it('renders correct guidance for indices with transforms', () => {
const wrapper = shallow(
<ReindexDetailsFlyoutStep
closeFlyout={jest.fn()}
startReindex={jest.fn()}
startReadonly={jest.fn()}
reindexState={defaultReindexState}
updateIndexState={defaultUpdateIndexState}
deprecation={{
...deprecation,
correctiveAction: { type: 'reindex', transformIds: ['abc', 'def'] },
}}
/>
);
expect(wrapper).toMatchInlineSnapshot(`
<Fragment>
<EuiFlyoutBody>
<EuiText>
<ESTransformsTargetGuidance
deprecation={
Object {
"correctiveAction": Object {
"transformIds": Array [
"abc",
"def",
],
"type": "reindex",
},
"isCritical": true,
"message": "foo",
"resolveDuringUpgrade": false,
"type": "index_settings",
"url": "https://te.st",
}
}
/>
</EuiText>
<EuiSpacer />
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup
justifyContent="spaceBetween"
>
<EuiFlexItem
grow={false}
>
<EuiButtonEmpty
flush="left"
iconType="cross"
onClick={[MockFunction]}
>
<MemoizedFormattedMessage
defaultMessage="Close"
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.closeButtonLabel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<EuiFlexGroup
gutterSize="s"
>
<EuiFlexItem
grow={false}
>
<EuiButton
color="primary"
data-test-subj="startReindexingButton"
disabled={false}
fill={true}
isLoading={false}
onClick={[MockFunction]}
>
<MemoizedFormattedMessage
defaultMessage="Start reindexing"
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.reindexButton.runReindexLabel"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</Fragment>
`);
});
it('renders for readonly indices (warning deprecation)', () => {
const props = cloneDeep(defaultReindexState);
props.meta.isReadonly = true;
@ -217,6 +314,7 @@ describe('ReindexDetailsFlyoutStep', () => {
startReadonly={jest.fn()}
reindexState={props}
updateIndexState={defaultUpdateIndexState}
deprecation={deprecation}
/>
);

View file

@ -23,7 +23,11 @@ import {
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { ReindexStatus } from '../../../../../../../../../common/types';
import {
EnrichedDeprecationInfo,
ReindexAction,
ReindexStatus,
} from '../../../../../../../../../common/types';
import { LoadingState } from '../../../../../../types';
import type { ReindexState } from '../../../use_reindex';
import { useAppContext } from '../../../../../../../app_context';
@ -32,6 +36,7 @@ import { FrozenCallOut } from '../frozen_callout';
import type { UpdateIndexState } from '../../../use_update_index';
import { FetchFailedCallOut } from '../fetch_failed_callout';
import { ReindexingFailedCallOut } from '../reindexing_failed_callout';
import { ESTransformsTargetGuidance } from './es_transform_target_guidance';
/**
* Displays a flyout that shows the details / corrective action for a "reindex" deprecation for a given index.
@ -39,10 +44,18 @@ import { ReindexingFailedCallOut } from '../reindexing_failed_callout';
export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
reindexState: ReindexState;
updateIndexState: UpdateIndexState;
deprecation: EnrichedDeprecationInfo;
startReindex: () => void;
startReadonly: () => void;
closeFlyout: () => void;
}> = ({ reindexState, updateIndexState, startReindex, startReadonly, closeFlyout }) => {
}> = ({
reindexState,
updateIndexState,
deprecation,
startReindex,
startReadonly,
closeFlyout,
}) => {
const {
services: {
api,
@ -57,6 +70,8 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
const isCompleted = reindexStatus === ReindexStatus.completed || updateIndexStatus === 'complete';
const hasFetchFailed = reindexStatus === ReindexStatus.fetchFailed;
const hasReindexingFailed = reindexStatus === ReindexStatus.failed;
const correctiveAction = deprecation.correctiveAction as ReindexAction | undefined;
const isESTransformTarget = !!correctiveAction?.transformIds?.length;
const { data: nodes } = api.useLoadNodeDiskSpace();
@ -145,7 +160,8 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
</p>
</Fragment>
)}
{!meta.isReadonly && (
{isESTransformTarget && <ESTransformsTargetGuidance deprecation={deprecation} />}
{!meta.isReadonly && !isESTransformTarget && (
<Fragment>
<p>
<FormattedMessage
@ -252,20 +268,24 @@ export const ReindexDetailsFlyoutStep: React.FunctionComponent<{
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s">
{!meta.isReadonly && !hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
<EuiFlexItem grow={false}>
<EuiButton
onClick={startReadonly}
disabled={loading}
data-test-subj="startIndexReadonlyButton"
>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton"
defaultMessage="Mark as read-only"
/>
</EuiButton>
</EuiFlexItem>
)}
{!meta.isReadonly &&
!hasFetchFailed &&
!isCompleted &&
hasRequiredPrivileges &&
!isESTransformTarget && (
<EuiFlexItem grow={false}>
<EuiButton
onClick={startReadonly}
disabled={loading}
data-test-subj="startIndexReadonlyButton"
>
<FormattedMessage
id="xpack.upgradeAssistant.esDeprecations.indices.indexFlyout.detailsStep.startIndexReadonlyButton"
defaultMessage="Mark as read-only"
/>
</EuiButton>
</EuiFlexItem>
)}
{!hasFetchFailed && !isCompleted && hasRequiredPrivileges && (
<EuiFlexItem grow={false}>
<EuiButton

View file

@ -164,6 +164,19 @@
"resolve_during_rolling_upgrade": false
}
],
"transforms_index": [
{
"level": "critical",
"message": "Old index with a compatibility version < 8.0",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
"details": "This index has version: 7.17.25",
"resolve_during_rolling_upgrade": false,
"_meta": {
"reindex_required": true,
"transform_ids": ["abc"]
}
}
],
"myindex": [
{
"level": "critical",

View file

@ -172,6 +172,22 @@ Object {
"type": "index_settings",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/6.0/breaking_60_mappings_changes.html#_coercion_of_boolean_fields",
},
Object {
"correctiveAction": Object {
"blockerForReindexing": undefined,
"transformIds": Array [
"abc",
],
"type": "reindex",
},
"details": "This index has version: 7.17.25",
"index": "transforms_index",
"isCritical": true,
"message": "Old index with a compatibility version < 8.0",
"resolveDuringUpgrade": false,
"type": "index_settings",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
},
Object {
"correctiveAction": Object {
"blockerForReindexing": undefined,
@ -216,7 +232,7 @@ Object {
"url": "https://github.com/elastic/elasticsearch/pull/117172",
},
],
"totalCriticalDeprecations": 8,
"totalCriticalDeprecations": 9,
"totalCriticalHealthIssues": 0,
}
`;

View file

@ -21,6 +21,13 @@ interface MlActionMetadata {
snapshot_id: string;
job_id: string;
}
interface IndexActionMetadata {
actions?: Action[];
reindex_required: boolean;
transform_ids: string[];
}
interface DataStreamActionMetadata {
actions?: Action[];
total_backing_indices: number;
@ -101,8 +108,10 @@ export const getCorrectiveAction = (
}
if (requiresReindexAction) {
const transformIds = (metadata as IndexActionMetadata)?.transform_ids;
return {
type: 'reindex',
...(transformIds?.length ? { transformIds } : {}),
};
}