[8.x] [UA] Support Deprecated Data Streams Migrations (#202204) (#202593)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[UA] Support Deprecated Data Streams Migrations
(#202204)](https://github.com/elastic/kibana/pull/202204)

<!--- Backport version: 9.4.3 -->

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

<!--BACKPORT [{"author":{"name":"Ahmad
Bamieh","email":"ahmad.bamyeh@elastic.co"},"sourceCommit":{"committedDate":"2024-12-02T21:53:24Z","message":"[UA]
Support Deprecated Data Streams Migrations (#202204)\n\n##
Summary\r\n\r\n- [x] Fix UA currently failing to return upgrade
status\r\n- [x] Support surfacing `data_streams` migrations in UA under
the ES tab\r\n- [x] Refactor code for better readablity\r\n- [x] Add
more test cases across the board for all the es migrations\r\nstatus
feature in UA\r\n- [x] Add a `featureSet.migrateDataStreams` to enable
surfacing data\r\nstreams migrations\r\n- [x] Surface data streams in UA
UI\r\n- [x] Take screenshots for a product review discussions\r\n- [x]
Unskip api_integration test cases\r\n\r\n### Imporant Notes\r\n\r\nES
deprecations are hidden behind the `featureSet` flag and will only
be\r\nshown in `8.last` for users.\r\nThis gives us time to review the
copy and implement the corrective\r\naction for reindexing data streams
which is still pending implementaiton\r\nfrom ES side.\r\n\r\nFor now we
will merge this to unblock upgrades in `8.17` and support\r\nsurfacing
data_streams deprecations and add tests.\r\n\r\nFollow up work for
`8.18`\r\n- Add integration Tests\r\n- Update copy of flyout and
documentation link\r\n- Reindexing data streams corrective
action\r\n\r\ncloses
https://github.com/elastic/kibana-team/issues/1293\r\n\r\n##
Screenshots\r\n\r\n#### Overview Page\r\n<img width=\"683\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/246d89ac-02cd-4813-ba38-e2e28df00c8d\">\r\n\r\n####
Elasticsearch deprecation issues Page\r\n\r\n<img width=\"1453\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/b5fd5f15-fa44-4acb-b7ff-4973593dcfbb\">\r\n\r\n\r\n####
Data streams deprecation details flyout\r\n<img width=\"778\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/af343f69-7e76-4c91-a6e3-cff29e26df59\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a6b3743e00add07c315eacadda4d0dbb532042ac","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","auto-backport","backport:prev-minor","backport:version","v8.17.0","v8.18.0"],"title":"[UA]
Support Deprecated Data Streams
Migrations","number":202204,"url":"https://github.com/elastic/kibana/pull/202204","mergeCommit":{"message":"[UA]
Support Deprecated Data Streams Migrations (#202204)\n\n##
Summary\r\n\r\n- [x] Fix UA currently failing to return upgrade
status\r\n- [x] Support surfacing `data_streams` migrations in UA under
the ES tab\r\n- [x] Refactor code for better readablity\r\n- [x] Add
more test cases across the board for all the es migrations\r\nstatus
feature in UA\r\n- [x] Add a `featureSet.migrateDataStreams` to enable
surfacing data\r\nstreams migrations\r\n- [x] Surface data streams in UA
UI\r\n- [x] Take screenshots for a product review discussions\r\n- [x]
Unskip api_integration test cases\r\n\r\n### Imporant Notes\r\n\r\nES
deprecations are hidden behind the `featureSet` flag and will only
be\r\nshown in `8.last` for users.\r\nThis gives us time to review the
copy and implement the corrective\r\naction for reindexing data streams
which is still pending implementaiton\r\nfrom ES side.\r\n\r\nFor now we
will merge this to unblock upgrades in `8.17` and support\r\nsurfacing
data_streams deprecations and add tests.\r\n\r\nFollow up work for
`8.18`\r\n- Add integration Tests\r\n- Update copy of flyout and
documentation link\r\n- Reindexing data streams corrective
action\r\n\r\ncloses
https://github.com/elastic/kibana-team/issues/1293\r\n\r\n##
Screenshots\r\n\r\n#### Overview Page\r\n<img width=\"683\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/246d89ac-02cd-4813-ba38-e2e28df00c8d\">\r\n\r\n####
Elasticsearch deprecation issues Page\r\n\r\n<img width=\"1453\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/b5fd5f15-fa44-4acb-b7ff-4973593dcfbb\">\r\n\r\n\r\n####
Data streams deprecation details flyout\r\n<img width=\"778\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/af343f69-7e76-4c91-a6e3-cff29e26df59\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a6b3743e00add07c315eacadda4d0dbb532042ac"}},"sourceBranch":"main","suggestedTargetBranches":["8.17","8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/202204","number":202204,"mergeCommit":{"message":"[UA]
Support Deprecated Data Streams Migrations (#202204)\n\n##
Summary\r\n\r\n- [x] Fix UA currently failing to return upgrade
status\r\n- [x] Support surfacing `data_streams` migrations in UA under
the ES tab\r\n- [x] Refactor code for better readablity\r\n- [x] Add
more test cases across the board for all the es migrations\r\nstatus
feature in UA\r\n- [x] Add a `featureSet.migrateDataStreams` to enable
surfacing data\r\nstreams migrations\r\n- [x] Surface data streams in UA
UI\r\n- [x] Take screenshots for a product review discussions\r\n- [x]
Unskip api_integration test cases\r\n\r\n### Imporant Notes\r\n\r\nES
deprecations are hidden behind the `featureSet` flag and will only
be\r\nshown in `8.last` for users.\r\nThis gives us time to review the
copy and implement the corrective\r\naction for reindexing data streams
which is still pending implementaiton\r\nfrom ES side.\r\n\r\nFor now we
will merge this to unblock upgrades in `8.17` and support\r\nsurfacing
data_streams deprecations and add tests.\r\n\r\nFollow up work for
`8.18`\r\n- Add integration Tests\r\n- Update copy of flyout and
documentation link\r\n- Reindexing data streams corrective
action\r\n\r\ncloses
https://github.com/elastic/kibana-team/issues/1293\r\n\r\n##
Screenshots\r\n\r\n#### Overview Page\r\n<img width=\"683\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/246d89ac-02cd-4813-ba38-e2e28df00c8d\">\r\n\r\n####
Elasticsearch deprecation issues Page\r\n\r\n<img width=\"1453\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/b5fd5f15-fa44-4acb-b7ff-4973593dcfbb\">\r\n\r\n\r\n####
Data streams deprecation details flyout\r\n<img width=\"778\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/af343f69-7e76-4c91-a6e3-cff29e26df59\">\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"a6b3743e00add07c315eacadda4d0dbb532042ac"}},{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Ahmad Bamieh <ahmad.bamyeh@elastic.co>
This commit is contained in:
Kibana Machine 2024-12-03 10:46:35 +11:00 committed by GitHub
parent bc826427c8
commit 53e01df17c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 743 additions and 502 deletions

View file

@ -354,6 +354,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.upgrade_assistant.featureSet.migrateSystemIndices (boolean?)',
'xpack.upgrade_assistant.featureSet.mlSnapshots (boolean?)',
'xpack.upgrade_assistant.featureSet.reindexCorrectiveActions (boolean?)',
'xpack.upgrade_assistant.featureSet.migrateDataStreams (boolean?)',
'xpack.upgrade_assistant.ui.enabled (boolean?)',
'xpack.observability.unsafe.alertDetails.metrics.enabled (boolean?)',
'xpack.observability.unsafe.alertDetails.logs.enabled (boolean?)',

View file

@ -15,6 +15,7 @@ Some features of the UA are only needed when upgrading to a new major version. T
* ML Snapshots (`featureSet.mlSnapshots`): Machine learning Upgrade mode can be toggled from outside Kibana, the purpose of this feature guard is to hide all ML related deprecations from the end user until the next major upgrade.
When we want to enable ML model snapshot deprecation warnings again we need to change the constant `MachineLearningField.MIN_CHECKED_SUPPORTED_SNAPSHOT_VERSION` to something higher than 7.0.0 in the Elasticsearch code.
* Migrating system indices (`featureSet.migrateSystemIndices`): Migrating system indices should only be enabled for major version upgrades. This config hides the second step from the UA UI for migrating system indices.
* Reindex Data Streams (`featureSet.migrateDataStreams`): Migrating deprecated Data streams should only be enabled for major version upgrades. The purpose of this feature guard is to hide all data streams related deprecations from the end user until the next major upgrade.
* Reindex corrective actions (`featureSet.reindexCorrectiveActions`): Deprecations with reindexing corrective actions are only enabled for major version upgrades. Currently, the reindex actions include some logic that is specific to the [8.0 upgrade](https://github.com/elastic/kibana/blob/main/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts). End users could get into a bad situation if this is enabled before this logic is fixed.
## Deprecations

View file

@ -80,6 +80,7 @@ export const getAppContextMock = (kibanaVersion: SemVer) => ({
featureSet: {
mlSnapshots: true,
migrateSystemIndices: true,
migrateDataStreams: true,
reindexCorrectiveActions: true,
},
kibanaVersionInfo: {

View file

@ -215,7 +215,7 @@ export interface HealthIndicatorAction {
export interface EnrichedDeprecationInfo
extends Omit<estypes.MigrationDeprecationsDeprecation, 'level'> {
type: keyof estypes.MigrationDeprecationsResponse | 'health_indicator';
type: keyof estypes.MigrationDeprecationsResponse | 'health_indicator' | 'data_streams';
isCritical: boolean;
status?: estypes.HealthReportIndicatorHealthStatus;
index?: string;
@ -296,4 +296,5 @@ export interface FeatureSet {
migrateSystemIndices: boolean;
mlSnapshots: boolean;
reindexCorrectiveActions: boolean;
migrateDataStreams: boolean;
}

View file

@ -6,8 +6,9 @@
*/
import { i18n } from '@kbn/i18n';
import { EnrichedDeprecationInfo } from '../../../common/types';
export const DEPRECATION_TYPE_MAP = {
export const DEPRECATION_TYPE_MAP: Record<EnrichedDeprecationInfo['type'], string> = {
cluster_settings: i18n.translate(
'xpack.upgradeAssistant.esDeprecations.clusterDeprecationTypeLabel',
{
@ -32,6 +33,9 @@ export const DEPRECATION_TYPE_MAP = {
defaultMessage: 'Health Indicator',
}
),
data_streams: i18n.translate('xpack.upgradeAssistant.esDeprecations.dataStreamsTypeLabel', {
defaultMessage: 'Data Stream',
}),
};
export const PAGINATION_CONFIG = {

View file

@ -47,6 +47,11 @@ const configSchema = schema.object({
* End users could get into a bad situation if this is enabled before this logic is fixed.
*/
reindexCorrectiveActions: schema.boolean({ defaultValue: false }),
/**
* Migrating deprecated data streams should only be enabled for major version upgrades.
* Currently this is manually set to `true` on every `x.last` version.
*/
migrateDataStreams: schema.boolean({ defaultValue: false }),
}),
/**
* This config allows to hide the UI without disabling the plugin.

View file

@ -0,0 +1,77 @@
/*
* 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.
*/
export const getMockEsDeprecations = () => {
return {
cluster_settings: [],
node_settings: [],
ml_settings: [],
index_settings: {},
data_streams: {},
};
};
export const getMockMlSettingsDeprecations = () => {
return {
ml_settings: [
{
level: 'warning',
message: 'Datafeed [deprecation-datafeed] uses deprecated query options',
url: 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes',
details:
'[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]',
// @ts-ignore
resolve_during_rolling_upgrade: false,
},
{
level: 'critical',
message:
'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded',
url: '',
details: 'details',
// @ts-ignore
_meta: { snapshot_id: '1', job_id: 'deprecation_check_job' },
// @ts-ignore
resolve_during_rolling_upgrade: false,
},
],
};
};
export const getMockDataStreamDeprecations = () => {
return {
data_streams: {
'my-v7-data-stream': [
{
level: 'critical',
message: 'Old data stream with a compatibility version < 8.0',
url: 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html',
details:
'This data stream has backing indices that were created before Elasticsearch 8.0.0',
resolve_during_rolling_upgrade: false,
_meta: {
backing_indices: {
count: 52,
need_upgrading: {
count: 37,
searchable_snapshot: {
count: 23,
fully_mounted: {
count: 7,
},
partially_mounted: {
count: 16,
},
},
},
},
},
},
],
},
};
};

View file

@ -148,5 +148,31 @@
"resolve_during_rolling_upgrade": false
}
]
},
"data_streams": {
"my-v7-data-stream" : [{
"level" : "critical",
"message" : "Old data stream with a compatibility version < 8.0",
"url" : "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
"details" : "This data stream has backing indices that were created before Elasticsearch 8.0.0",
"resolve_during_rolling_upgrade" : false,
"_meta": {
"backing_indices": {
"count": 52,
"need_upgrading": {
"count": 37,
"searchable_snapshot": {
"count": 23,
"fully_mounted": {
"count": 7
},
"partially_mounted": {
"count": 16
}
}
}
}
}
}]
}
}

View file

@ -1,340 +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 type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { IScopedClusterClient } from '@kbn/core/server';
import { i18n } from '@kbn/i18n';
import { EnrichedDeprecationInfo, ESUpgradeStatus, FeatureSet } from '../../common/types';
import { esIndicesStateCheck } from './es_indices_state_check';
import {
getESSystemIndicesMigrationStatus,
convertFeaturesToIndicesArray,
} from './es_system_indices_migration';
export function getShardCapacityDeprecationInfo({
symptom,
details,
}: {
details: any;
symptom: any;
}) {
// When we dont have a details field for our indicator, we can only report
// the symptom to the user given that's the only information about the deprecation
// we have.
if (!details) {
return {
details: symptom,
message: symptom,
url: null,
resolveDuringUpgrade: false,
};
}
const causes = [];
if (details.indices_with_readonly_block > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.indicesWithReadonlyBlockCauseMessage',
{
defaultMessage:
'The number of indices the system enforced a read-only index block (`index.blocks.read_only_allow_delete`) on because the cluster is running out of space.',
}
)
);
}
if (details.nodes_over_high_watermark > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.nodesOverHighWatermarkCauseMessage',
{
defaultMessage:
'The number of nodes that are running low on disk and it is likely that they will run out of space. Their disk usage has tripped the <<cluster-routing-watermark-high, high watermark threshold>>.',
ignoreTag: true,
}
)
);
}
if (details.nodes_over_flood_stage_watermark > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.nodesOverFloodStageWatermarkCauseMessage',
{
defaultMessage:
'The number of nodes that have run out of disk. Their disk usage has tripped the <<cluster-routing-flood-stage, flood stagewatermark threshold>>.',
ignoreTag: true,
}
)
);
}
return {
details: symptom,
message: symptom,
url: null,
resolveDuringUpgrade: false,
correctiveAction: {
type: 'healthIndicator',
impacts: details,
cause: causes.join('\n'),
},
};
}
export async function getHealthIndicators(
dataClient: IScopedClusterClient
): Promise<EnrichedDeprecationInfo[]> {
const healthIndicators = await dataClient.asCurrentUser.healthReport();
const isStatusNotGreen = (indicator?: estypes.HealthReportBaseIndicator): boolean => {
return !!(indicator?.status && indicator?.status !== 'green');
};
// Temporarily ignoring due to untyped ES indicators
// types will be available during 8.9.0
// @ts-ignore
return [
...[
// @ts-ignore
healthIndicators.indicators.shards_capacity as estypes.HealthReportBaseIndicator,
]
.filter(isStatusNotGreen)
.flatMap(({ status, symptom, impacts, diagnosis }) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
return (diagnosis || []).map(({ cause, action, help_url }) => ({
type: 'health_indicator',
details: symptom,
message: cause,
url: help_url,
isCritical: status === 'red',
resolveDuringUpgrade: false,
correctiveAction: { type: 'healthIndicator', cause, action, impacts },
}));
}),
...[healthIndicators.indicators.disk as estypes.HealthReportDiskIndicator]
.filter(isStatusNotGreen)
.flatMap(({ status, symptom, details }) => {
return {
type: 'health_indicator',
isCritical: status === 'red',
...getShardCapacityDeprecationInfo({ symptom, details }),
};
}),
];
}
export async function getESUpgradeStatus(
dataClient: IScopedClusterClient,
featureSet: FeatureSet
): Promise<ESUpgradeStatus> {
const getCombinedDeprecations = async () => {
const healthIndicators = await getHealthIndicators(dataClient);
const deprecations = await dataClient.asCurrentUser.migration.deprecations();
const indices = await getCombinedIndexInfos(deprecations, dataClient);
const systemIndices = await getESSystemIndicesMigrationStatus(dataClient.asCurrentUser);
const systemIndicesList = convertFeaturesToIndicesArray(systemIndices.features);
const enrichedDeprecations = Object.keys(deprecations).reduce(
(combinedDeprecations, deprecationType) => {
if (deprecationType === 'index_settings') {
// We need to exclude all index related deprecations for system indices since
// they are resolved separately through the system indices upgrade section in
// the Overview page.
const withoutSystemIndices = indices.filter(
(index) => !systemIndicesList.includes(index.index!)
);
combinedDeprecations = combinedDeprecations.concat(withoutSystemIndices);
} else {
const deprecationsByType = deprecations[
deprecationType as keyof estypes.MigrationDeprecationsResponse
] as estypes.MigrationDeprecationsDeprecation[];
const enrichedDeprecationInfo = deprecationsByType
.map(
({
details,
level,
message,
url,
// @ts-expect-error @elastic/elasticsearch _meta not available yet in MigrationDeprecationInfoResponse
_meta: metadata,
// @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse
resolve_during_rolling_upgrade: resolveDuringUpgrade,
}) => {
return {
details,
message,
url,
type: deprecationType as keyof estypes.MigrationDeprecationsResponse,
isCritical: level === 'critical',
resolveDuringUpgrade,
correctiveAction: getCorrectiveAction(message, metadata),
};
}
)
.filter(({ correctiveAction, type }) => {
/**
* This disables showing the ML deprecations in the UA if `featureSet.mlSnapshots`
* is set to `false`.
*
* This config should be set to true only on the `x.last` versions, or when
* the constant `MachineLearningField.MIN_CHECKED_SUPPORTED_SNAPSHOT_VERSION`
* is incremented to something higher than 7.0.0 in the Elasticsearch code.
*/
if (!featureSet.mlSnapshots) {
if (type === 'ml_settings' || correctiveAction?.type === 'mlSnapshot') {
return false;
}
}
/**
* This disables showing the reindexing deprecations in the UA if
* `featureSet.reindexCorrectiveActions` is set to `false`.
*/
if (!featureSet.reindexCorrectiveActions && correctiveAction?.type === 'reindex') {
return false;
}
return true;
});
combinedDeprecations = combinedDeprecations.concat(enrichedDeprecationInfo);
}
return combinedDeprecations;
},
[] as EnrichedDeprecationInfo[]
);
const enrichedHealthIndicators = healthIndicators.filter(({ status }) => {
return status !== 'green';
}) as EnrichedDeprecationInfo[];
return [...enrichedHealthIndicators, ...enrichedDeprecations];
};
const combinedDeprecations = await getCombinedDeprecations();
const criticalWarnings = combinedDeprecations.filter(({ isCritical }) => isCritical === true);
return {
totalCriticalDeprecations: criticalWarnings.length,
deprecations: combinedDeprecations,
};
}
// Reformats the index deprecations to an array of deprecation warnings extended with an index field.
const getCombinedIndexInfos = async (
deprecations: estypes.MigrationDeprecationsResponse,
dataClient: IScopedClusterClient
) => {
const indices = Object.keys(deprecations.index_settings).reduce(
(indexDeprecations, indexName) => {
return indexDeprecations.concat(
deprecations.index_settings[indexName].map(
({
details,
message,
url,
level,
// @ts-expect-error @elastic/elasticsearch _meta not available yet in MigrationDeprecationInfoResponse
_meta: metadata,
// @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse
resolve_during_rolling_upgrade: resolveDuringUpgrade,
}) =>
({
details,
message,
url,
index: indexName,
type: 'index_settings',
isCritical: level === 'critical',
correctiveAction: getCorrectiveAction(message, metadata, indexName),
resolveDuringUpgrade,
} as EnrichedDeprecationInfo)
)
);
},
[] as EnrichedDeprecationInfo[]
);
const indexNames = indices.map(({ index }) => index!);
// If we have found deprecation information for index/indices
// check whether the index is open or closed.
if (indexNames.length) {
const indexStates = await esIndicesStateCheck(dataClient.asCurrentUser, indexNames);
indices.forEach((indexData) => {
if (indexData.correctiveAction?.type === 'reindex') {
indexData.correctiveAction.blockerForReindexing =
indexStates[indexData.index!] === 'closed' ? 'index-closed' : undefined;
}
});
}
return indices as EnrichedDeprecationInfo[];
};
interface Action {
action_type: 'remove_settings';
objects: string[];
}
interface Actions {
actions: Action[];
}
type EsMetadata = Actions & {
[key: string]: string;
};
const getCorrectiveAction = (
message: string,
metadata: EsMetadata,
indexName?: string
): EnrichedDeprecationInfo['correctiveAction'] => {
const indexSettingDeprecation = metadata?.actions?.find(
(action) => action.action_type === 'remove_settings' && indexName
);
const clusterSettingDeprecation = metadata?.actions?.find(
(action) => action.action_type === 'remove_settings' && typeof indexName === 'undefined'
);
const requiresReindexAction = /Index created before/.test(message);
const requiresIndexSettingsAction = Boolean(indexSettingDeprecation);
const requiresClusterSettingsAction = Boolean(clusterSettingDeprecation);
const requiresMlAction = /[Mm]odel snapshot/.test(message);
if (requiresReindexAction) {
return {
type: 'reindex',
};
}
if (requiresIndexSettingsAction) {
return {
type: 'indexSetting',
deprecatedSettings: indexSettingDeprecation!.objects,
};
}
if (requiresClusterSettingsAction) {
return {
type: 'clusterSetting',
deprecatedSettings: clusterSettingDeprecation!.objects,
};
}
if (requiresMlAction) {
const { snapshot_id: snapshotId, job_id: jobId } = metadata!;
return {
type: 'mlSnapshot',
snapshotId,
jobId,
};
}
};

View file

@ -6,6 +6,7 @@ Object {
Object {
"correctiveAction": undefined,
"details": "templates using \`template\` field: security_audit_log,watches,.monitoring-alerts,triggered_watches,.ml-anomalies-,.ml-notifications,.ml-meta,.monitoring-kibana,.monitoring-es,.monitoring-logstash,.watch-history-6,.ml-state,security-index-template",
"index": undefined,
"isCritical": false,
"message": "Template patterns are no longer using \`template\` field, but \`index_patterns\` instead",
"resolveDuringUpgrade": false,
@ -15,6 +16,7 @@ Object {
Object {
"correctiveAction": undefined,
"details": "{.monitoring-logstash=[Coercion of boolean fields], .monitoring-es=[Coercion of boolean fields], .ml-anomalies-=[Coercion of boolean fields], .watch-history-6=[Coercion of boolean fields], .monitoring-kibana=[Coercion of boolean fields], security-index-template=[Coercion of boolean fields]}",
"index": undefined,
"isCritical": false,
"message": "one or more templates use deprecated mapping settings",
"resolveDuringUpgrade": false,
@ -24,6 +26,7 @@ Object {
Object {
"correctiveAction": undefined,
"details": "[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]",
"index": undefined,
"isCritical": false,
"message": "Datafeed [deprecation-datafeed] uses deprecated query options",
"resolveDuringUpgrade": false,
@ -37,6 +40,7 @@ Object {
"type": "mlSnapshot",
},
"details": "details",
"index": undefined,
"isCritical": true,
"message": "model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded",
"resolveDuringUpgrade": false,
@ -46,6 +50,7 @@ Object {
Object {
"correctiveAction": undefined,
"details": "This node thing is wrong",
"index": undefined,
"isCritical": true,
"message": "A node-level issue",
"resolveDuringUpgrade": true,
@ -153,7 +158,17 @@ 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": undefined,
"details": "This data stream has backing indices that were created before Elasticsearch 8.0.0",
"index": "my-v7-data-stream",
"isCritical": true,
"message": "Old data stream with a compatibility version < 8.0",
"resolveDuringUpgrade": false,
"type": "data_streams",
"url": "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-9.0.html",
},
],
"totalCriticalDeprecations": 4,
"totalCriticalDeprecations": 5,
}
`;

View file

@ -0,0 +1,68 @@
/*
* 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 { EnrichedDeprecationInfo } from '../../../common/types';
interface Action {
action_type: 'remove_settings';
objects: string[];
}
interface Actions {
actions: Action[];
}
type EsMetadata = Actions & {
[key: string]: string;
};
export const getCorrectiveAction = (
message: string,
metadata: EsMetadata,
indexName?: string
): EnrichedDeprecationInfo['correctiveAction'] => {
const indexSettingDeprecation = metadata?.actions?.find(
(action) => action.action_type === 'remove_settings' && indexName
);
const clusterSettingDeprecation = metadata?.actions?.find(
(action) => action.action_type === 'remove_settings' && typeof indexName === 'undefined'
);
const requiresReindexAction = /Index created before/.test(message);
const requiresIndexSettingsAction = Boolean(indexSettingDeprecation);
const requiresClusterSettingsAction = Boolean(clusterSettingDeprecation);
const requiresMlAction = /[Mm]odel snapshot/.test(message);
if (requiresReindexAction) {
return {
type: 'reindex',
};
}
if (requiresIndexSettingsAction) {
return {
type: 'indexSetting',
deprecatedSettings: indexSettingDeprecation!.objects,
};
}
if (requiresClusterSettingsAction) {
return {
type: 'clusterSetting',
deprecatedSettings: clusterSettingDeprecation!.objects,
};
}
if (requiresMlAction) {
const { snapshot_id: snapshotId, job_id: jobId } = metadata!;
return {
type: 'mlSnapshot',
snapshotId,
jobId,
};
}
};

View file

@ -0,0 +1,131 @@
/*
* 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 { elasticsearchServiceMock, ScopedClusterClientMock } from '@kbn/core/server/mocks';
import { getHealthIndicators } from './health_indicators';
import * as healthIndicatorsMock from '../__fixtures__/health_indicators';
describe('getHealthIndicators', () => {
let esClient: ScopedClusterClientMock;
beforeEach(() => {
esClient = elasticsearchServiceMock.createScopedClusterClient();
});
it('returns empty array on green indicators', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorGreen,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toEqual([]);
});
it('returns unknown indicators', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorUnknown,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result[0]).toEqual(
expect.objectContaining({
details: 'No disk usage data.',
})
);
});
it('returns unhealthy shards_capacity indicator', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorGreen,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorRed,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"correctiveAction": Object {
"action": "Increase the value of [cluster.max_shards_per_node] cluster setting or remove data indices to clear up resources.",
"cause": "Elasticsearch is about to reach the maximum number of shards it can host, based on your current settings.",
"impacts": Array [
Object {
"description": "The cluster has too many used shards to be able to upgrade.",
"id": "elasticsearch:health:shards_capacity:impact:upgrade_blocked",
"impact_areas": "[Array]",
"severity": 1,
},
Object {
"description": "The cluster is running low on room to add new shards. Adding data to new indices is at risk",
"id": "elasticsearch:health:shards_capacity:impact:creation_of_new_indices_blocked",
"impact_areas": "[Array]",
"severity": 1,
},
],
"type": "healthIndicator",
},
"details": "Cluster is close to reaching the configured maximum number of shards for data nodes.",
"isCritical": true,
"message": "Elasticsearch is about to reach the maximum number of shards it can host, based on your current settings.",
"resolveDuringUpgrade": false,
"type": "health_indicator",
"url": "https://ela.st/fix-shards-capacity",
},
]
`);
});
it('returns unhealthy disk indicator', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorRed,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"correctiveAction": Object {
"cause": "The number of indices the system enforced a read-only index block (\`index.blocks.read_only_allow_delete\`) on because the cluster is running out of space.
The number of nodes that are running low on disk and it is likely that they will run out of space. Their disk usage has tripped the <<cluster-routing-watermark-high, high watermark threshold>>.
The number of nodes that have run out of disk. Their disk usage has tripped the <<cluster-routing-flood-stage, flood stagewatermark threshold>>.",
"impacts": Object {
"indices_with_readonly_block": 1,
"nodes_over_flood_stage_watermark": 1,
"nodes_over_high_watermark": 1,
"nodes_with_enough_disk_space": 1,
"nodes_with_unknown_disk_status": 1,
},
"type": "healthIndicator",
},
"details": "The cluster does not have enough available disk space.",
"isCritical": true,
"message": "The cluster does not have enough available disk space.",
"resolveDuringUpgrade": false,
"type": "health_indicator",
"url": null,
},
]
`);
});
});

View file

@ -0,0 +1,123 @@
/*
* 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 type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { IScopedClusterClient } from '@kbn/core/server';
import { EnrichedDeprecationInfo } from '../../../common/types';
export async function getHealthIndicators(
dataClient: IScopedClusterClient
): Promise<EnrichedDeprecationInfo[]> {
const healthIndicators = await dataClient.asCurrentUser.healthReport();
const isStatusNotGreen = (indicator?: estypes.HealthReportBaseIndicator): boolean => {
return !!(indicator?.status && indicator?.status !== 'green');
};
// Temporarily ignoring due to untyped ES indicators
// types will be available during 8.9.0
// @ts-ignore
return [
...[
// @ts-ignore
healthIndicators.indicators.shards_capacity as estypes.HealthReportBaseIndicator,
]
.filter(isStatusNotGreen)
.flatMap(({ status, symptom, impacts, diagnosis }) => {
// eslint-disable-next-line @typescript-eslint/naming-convention
return (diagnosis || []).map(({ cause, action, help_url }) => ({
type: 'health_indicator',
details: symptom,
message: cause,
url: help_url,
isCritical: status === 'red',
resolveDuringUpgrade: false,
correctiveAction: { type: 'healthIndicator', cause, action, impacts },
}));
}),
...[healthIndicators.indicators.disk as estypes.HealthReportDiskIndicator]
.filter(isStatusNotGreen)
.flatMap(({ status, symptom, details }) => {
return {
type: 'health_indicator',
isCritical: status === 'red',
...getShardCapacityDeprecationInfo({ symptom, details }),
};
}),
];
}
export function getShardCapacityDeprecationInfo({
symptom,
details,
}: {
details: any;
symptom: any;
}) {
// When we dont have a details field for our indicator, we can only report
// the symptom to the user given that's the only information about the deprecation
// we have.
if (!details) {
return {
details: symptom,
message: symptom,
url: null,
resolveDuringUpgrade: false,
};
}
const causes = [];
if (details.indices_with_readonly_block > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.indicesWithReadonlyBlockCauseMessage',
{
defaultMessage:
'The number of indices the system enforced a read-only index block (`index.blocks.read_only_allow_delete`) on because the cluster is running out of space.',
}
)
);
}
if (details.nodes_over_high_watermark > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.nodesOverHighWatermarkCauseMessage',
{
defaultMessage:
'The number of nodes that are running low on disk and it is likely that they will run out of space. Their disk usage has tripped the <<cluster-routing-watermark-high, high watermark threshold>>.',
ignoreTag: true,
}
)
);
}
if (details.nodes_over_flood_stage_watermark > 0) {
causes.push(
i18n.translate(
'xpack.upgradeAssistant.esDeprecationsStatus.nodesOverFloodStageWatermarkCauseMessage',
{
defaultMessage:
'The number of nodes that have run out of disk. Their disk usage has tripped the <<cluster-routing-flood-stage, flood stagewatermark threshold>>.',
ignoreTag: true,
}
)
);
}
return {
details: symptom,
message: symptom,
url: null,
resolveDuringUpgrade: false,
correctiveAction: {
type: 'healthIndicator',
impacts: details,
cause: causes.join('\n'),
},
};
}

View file

@ -6,142 +6,22 @@
*/
import _ from 'lodash';
import { elasticsearchServiceMock, ScopedClusterClientMock } from '@kbn/core/server/mocks';
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { getESUpgradeStatus, getHealthIndicators } from './es_deprecations_status';
import fakeDeprecations from './__fixtures__/fake_deprecations.json';
import * as healthIndicatorsMock from './__fixtures__/health_indicators';
import type { FeatureSet } from '../../common/types';
import fakeDeprecations from '../__fixtures__/fake_deprecations.json';
import * as healthIndicatorsMock from '../__fixtures__/health_indicators';
import * as esMigrationsMock from '../__fixtures__/es_deprecations';
import type { FeatureSet } from '../../../common/types';
import { getESUpgradeStatus } from '.';
const fakeIndexNames = Object.keys(fakeDeprecations.index_settings);
describe('getHealthIndicators', () => {
let esClient: ScopedClusterClientMock;
beforeEach(() => {
esClient = elasticsearchServiceMock.createScopedClusterClient();
});
it('returns empty array on green indicators', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorGreen,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toEqual([]);
});
it('returns unknown indicators', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorUnknown,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result[0]).toEqual(
expect.objectContaining({
details: 'No disk usage data.',
})
);
});
it('returns unhealthy shards_capacity indicator', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorGreen,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorRed,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"correctiveAction": Object {
"action": "Increase the value of [cluster.max_shards_per_node] cluster setting or remove data indices to clear up resources.",
"cause": "Elasticsearch is about to reach the maximum number of shards it can host, based on your current settings.",
"impacts": Array [
Object {
"description": "The cluster has too many used shards to be able to upgrade.",
"id": "elasticsearch:health:shards_capacity:impact:upgrade_blocked",
"impact_areas": "[Array]",
"severity": 1,
},
Object {
"description": "The cluster is running low on room to add new shards. Adding data to new indices is at risk",
"id": "elasticsearch:health:shards_capacity:impact:creation_of_new_indices_blocked",
"impact_areas": "[Array]",
"severity": 1,
},
],
"type": "healthIndicator",
},
"details": "Cluster is close to reaching the configured maximum number of shards for data nodes.",
"isCritical": true,
"message": "Elasticsearch is about to reach the maximum number of shards it can host, based on your current settings.",
"resolveDuringUpgrade": false,
"type": "health_indicator",
"url": "https://ela.st/fix-shards-capacity",
},
]
`);
});
it('returns unhealthy disk indicator', async () => {
esClient.asCurrentUser.healthReport.mockResponse({
cluster_name: 'mock',
indicators: {
disk: healthIndicatorsMock.diskIndicatorRed,
// @ts-ignore
shards_capacity: healthIndicatorsMock.shardCapacityIndicatorGreen,
},
});
const result = await getHealthIndicators(esClient);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"correctiveAction": Object {
"cause": "The number of indices the system enforced a read-only index block (\`index.blocks.read_only_allow_delete\`) on because the cluster is running out of space.
The number of nodes that are running low on disk and it is likely that they will run out of space. Their disk usage has tripped the <<cluster-routing-watermark-high, high watermark threshold>>.
The number of nodes that have run out of disk. Their disk usage has tripped the <<cluster-routing-flood-stage, flood stagewatermark threshold>>.",
"impacts": Object {
"indices_with_readonly_block": 1,
"nodes_over_flood_stage_watermark": 1,
"nodes_over_high_watermark": 1,
"nodes_with_enough_disk_space": 1,
"nodes_with_unknown_disk_status": 1,
},
"type": "healthIndicator",
},
"details": "The cluster does not have enough available disk space.",
"isCritical": true,
"message": "The cluster does not have enough available disk space.",
"resolveDuringUpgrade": false,
"type": "health_indicator",
"url": null,
},
]
`);
});
});
describe('getESUpgradeStatus', () => {
const featureSet: FeatureSet = {
reindexCorrectiveActions: true,
migrateSystemIndices: true,
mlSnapshots: true,
migrateDataStreams: true,
};
const resolvedIndices = {
@ -200,6 +80,7 @@ describe('getESUpgradeStatus', () => {
node_settings: [],
ml_settings: [],
index_settings: {},
data_streams: {},
});
await expect(getESUpgradeStatus(esClient, featureSet)).resolves.toHaveProperty(
@ -215,6 +96,7 @@ describe('getESUpgradeStatus', () => {
node_settings: [],
ml_settings: [],
index_settings: {},
data_streams: {},
});
await expect(getESUpgradeStatus(esClient, featureSet)).resolves.toHaveProperty(
@ -240,6 +122,7 @@ describe('getESUpgradeStatus', () => {
},
],
},
data_streams: {},
});
const upgradeStatus = await getESUpgradeStatus(esClient, featureSet);
@ -249,38 +132,44 @@ describe('getESUpgradeStatus', () => {
});
it('filters out ml_settings if featureSet.mlSnapshots is set to false', async () => {
esClient.asCurrentUser.migration.deprecations.mockResponse({
cluster_settings: [],
node_settings: [],
ml_settings: [
{
level: 'warning',
message: 'Datafeed [deprecation-datafeed] uses deprecated query options',
url: 'https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes',
details:
'[Deprecated field [use_dis_max] used, replaced by [Set [tie_breaker] to 1 instead]]',
// @ts-ignore
resolve_during_rolling_upgrade: false,
},
{
level: 'critical',
message:
'model snapshot [1] for job [deprecation_check_job] needs to be deleted or upgraded',
url: '',
details: 'details',
// @ts-ignore
_meta: { snapshot_id: '1', job_id: 'deprecation_check_job' },
// @ts-ignore
resolve_during_rolling_upgrade: false,
},
],
index_settings: {},
const mockResponse = {
...esMigrationsMock.getMockEsDeprecations(),
...esMigrationsMock.getMockMlSettingsDeprecations(),
};
// @ts-ignore missing property definitions in ES resolve_during_rolling_upgrade and _meta
esClient.asCurrentUser.migration.deprecations.mockResponse(mockResponse);
const enabledUpgradeStatus = await getESUpgradeStatus(esClient, { ...featureSet });
expect(enabledUpgradeStatus.deprecations).toHaveLength(2);
expect(enabledUpgradeStatus.totalCriticalDeprecations).toBe(1);
const disabledUpgradeStatus = await getESUpgradeStatus(esClient, {
...featureSet,
mlSnapshots: false,
});
const upgradeStatus = await getESUpgradeStatus(esClient, { ...featureSet, mlSnapshots: false });
expect(disabledUpgradeStatus.deprecations).toHaveLength(0);
expect(disabledUpgradeStatus.totalCriticalDeprecations).toBe(0);
});
expect(upgradeStatus.deprecations).toHaveLength(0);
expect(upgradeStatus.totalCriticalDeprecations).toBe(0);
it('filters out data_streams if featureSet.migrateDataStreams is set to false', async () => {
const mockResponse = {
...esMigrationsMock.getMockEsDeprecations(),
...esMigrationsMock.getMockDataStreamDeprecations(),
};
esClient.asCurrentUser.migration.deprecations.mockResponse(mockResponse);
const enabledUpgradeStatus = await getESUpgradeStatus(esClient, { ...featureSet });
expect(enabledUpgradeStatus.deprecations).toHaveLength(1);
expect(enabledUpgradeStatus.totalCriticalDeprecations).toBe(1);
const disabledUpgradeStatus = await getESUpgradeStatus(esClient, {
...featureSet,
migrateDataStreams: false,
});
expect(disabledUpgradeStatus.deprecations).toHaveLength(0);
expect(disabledUpgradeStatus.totalCriticalDeprecations).toBe(0);
});
it('filters out reindex corrective actions if featureSet.reindexCorrectiveActions is set to false', async () => {
@ -306,6 +195,7 @@ describe('getESUpgradeStatus', () => {
],
ml_settings: [],
index_settings: {},
data_streams: {},
});
const upgradeStatus = await getESUpgradeStatus(esClient, {
@ -332,6 +222,7 @@ describe('getESUpgradeStatus', () => {
],
ml_settings: [],
index_settings: {},
data_streams: {},
});
esClient.asCurrentUser.healthReport.mockResponse({
@ -380,6 +271,7 @@ describe('getESUpgradeStatus', () => {
"type": "reindex",
},
"details": "This index was created using version: 6.8.13",
"index": undefined,
"isCritical": true,
"message": "Index created before 7.0",
"resolveDuringUpgrade": false,

View file

@ -0,0 +1,69 @@
/*
* 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 { IScopedClusterClient } from '@kbn/core/server';
import { EnrichedDeprecationInfo, ESUpgradeStatus, FeatureSet } from '../../../common/types';
import { getEnrichedDeprecations } from './migrations';
import { getHealthIndicators } from './health_indicators';
export async function getESUpgradeStatus(
dataClient: IScopedClusterClient,
featureSet: FeatureSet
): Promise<ESUpgradeStatus> {
const getCombinedDeprecations = async () => {
const healthIndicators = await getHealthIndicators(dataClient);
const enrichedDeprecations = await getEnrichedDeprecations(dataClient);
const toggledMigrationsDeprecations = enrichedDeprecations.filter(
({ type, correctiveAction }) => {
/**
* This disables showing the ML deprecations in the UA if `featureSet.mlSnapshots`
* is set to `false`.
*
* This config should be set to true only on the `x.last` versions, or when
* the constant `MachineLearningField.MIN_CHECKED_SUPPORTED_SNAPSHOT_VERSION`
* is incremented to something higher than 7.0.0 in the Elasticsearch code.
*/
if (type === 'ml_settings' || correctiveAction?.type === 'mlSnapshot') {
return featureSet.mlSnapshots;
}
/**
* This disables showing the Data streams deprecations in the UA if
* `featureSet.migrateDataStreams` is set to `false`.
*/
if (type === 'data_streams') {
return !!featureSet.migrateDataStreams;
}
/**
* This disables showing the reindexing deprecations in the UA if
* `featureSet.reindexCorrectiveActions` is set to `false`.
*/
if (correctiveAction?.type === 'reindex') {
return !!featureSet.reindexCorrectiveActions;
}
return true;
}
);
const enrichedHealthIndicators = healthIndicators.filter(({ status }) => {
return status !== 'green';
}) as EnrichedDeprecationInfo[];
return [...enrichedHealthIndicators, ...toggledMigrationsDeprecations];
};
const combinedDeprecations = await getCombinedDeprecations();
const criticalWarnings = combinedDeprecations.filter(({ isCritical }) => isCritical === true);
return {
totalCriticalDeprecations: criticalWarnings.length,
deprecations: combinedDeprecations,
};
}

View file

@ -0,0 +1,157 @@
/*
* 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 type {
MigrationDeprecationsResponse,
MigrationDeprecationsDeprecation,
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server';
import _ from 'lodash';
import { EnrichedDeprecationInfo } from '../../../common/types';
import {
convertFeaturesToIndicesArray,
getESSystemIndicesMigrationStatus,
} from '../es_system_indices_migration';
import { getCorrectiveAction } from './get_corrective_actions';
import { esIndicesStateCheck } from '../es_indices_state_check';
/**
* Remove once the data_streams type is added to the `MigrationDeprecationsResponse` type
*/
interface EsDeprecations extends MigrationDeprecationsResponse {
data_streams: Record<string, MigrationDeprecationsDeprecation[]>;
}
const createBaseMigrationDeprecation = (
migrationDeprecation: MigrationDeprecationsDeprecation,
{ deprecationType, indexName }: { deprecationType: keyof EsDeprecations; indexName?: string }
) => {
const {
details,
message,
url,
level,
// @ts-expect-error @elastic/elasticsearch _meta not available yet in MigrationDeprecationInfoResponse
_meta: metadata,
// @ts-expect-error @elastic/elasticsearch resolve_during_rolling_upgrade not available yet in MigrationDeprecationInfoResponse
resolve_during_rolling_upgrade: resolveDuringUpgrade,
} = migrationDeprecation;
return {
index: indexName,
type: deprecationType,
details,
message,
url,
isCritical: level === 'critical',
metadata,
resolveDuringUpgrade,
};
};
const normalizeEsResponse = (migrationsResponse: EsDeprecations) => {
const indexSettingsMigrations = Object.entries(migrationsResponse.index_settings).flatMap(
([indexName, migrationDeprecations]) => {
return migrationDeprecations.flatMap((migrationDeprecation) =>
createBaseMigrationDeprecation(migrationDeprecation, {
indexName,
deprecationType: 'index_settings',
})
);
}
);
const dataStreamsMigrations = Object.entries(migrationsResponse.data_streams).flatMap(
([indexName, dataStreamDeprecations]) => {
return dataStreamDeprecations.flatMap((depractionData) =>
createBaseMigrationDeprecation(depractionData, {
indexName,
deprecationType: 'data_streams',
})
);
}
);
const mlSettingsMigrations = migrationsResponse.ml_settings.map((depractionData) =>
createBaseMigrationDeprecation(depractionData, { deprecationType: 'ml_settings' })
);
const nodeSettingsMigrations = migrationsResponse.node_settings.map((depractionData) =>
createBaseMigrationDeprecation(depractionData, { deprecationType: 'node_settings' })
);
const clusterSettingsMigrations = migrationsResponse.cluster_settings.map((depractionData) =>
createBaseMigrationDeprecation(depractionData, { deprecationType: 'cluster_settings' })
);
return [
...clusterSettingsMigrations,
...mlSettingsMigrations,
...nodeSettingsMigrations,
...indexSettingsMigrations,
...dataStreamsMigrations,
].flat();
};
export const getEnrichedDeprecations = async (
dataClient: IScopedClusterClient
): Promise<EnrichedDeprecationInfo[]> => {
const deprecations = (await dataClient.asCurrentUser.migration.deprecations()) as EsDeprecations;
const systemIndices = await getESSystemIndicesMigrationStatus(dataClient.asCurrentUser);
const systemIndicesList = convertFeaturesToIndicesArray(systemIndices.features);
const indexSettingsIndexNames = Object.keys(deprecations.index_settings).map(
(indexName) => indexName!
);
const indexSettingsIndexStates = indexSettingsIndexNames.length
? await esIndicesStateCheck(dataClient.asCurrentUser, indexSettingsIndexNames)
: {};
return normalizeEsResponse(deprecations)
.filter((deprecation) => {
switch (deprecation.type) {
case 'index_settings': {
if (!deprecation.index) {
return false;
}
// filter out system indices
return !systemIndicesList.includes(deprecation.index);
}
case 'cluster_settings':
case 'ml_settings':
case 'node_settings':
case 'data_streams': {
return true;
}
default: {
// Throwing here to avoid allowing upgrades while we have unhandled deprecation types from ES
// That might cause the stack to fail to start after upgrade.
throw new Error(`Unknown ES deprecation type "${deprecation.type}"`);
}
}
})
.map((deprecation) => {
const correctiveAction = getCorrectiveAction(
deprecation.message,
deprecation.metadata,
deprecation.index!
);
// If we have found deprecation information for index/indices
// check whether the index is open or closed.
if (deprecation.type === 'index_settings' && correctiveAction?.type === 'reindex') {
correctiveAction.blockerForReindexing =
indexSettingsIndexStates[deprecation.index!] === 'closed' ? 'index-closed' : undefined;
}
const enrichedDeprecation = _.omit(deprecation, 'metadata');
return {
...enrichedDeprecation,
correctiveAction,
};
});
};

View file

@ -42,7 +42,8 @@
"@kbn/deeplinks-observability",
"@kbn/core-logging-server-mocks",
"@kbn/core-http-server-mocks",
"@kbn/core-http-server-utils"
"@kbn/core-http-server-utils",
"@kbn/core-elasticsearch-server"
],
"exclude": [
"target/**/*",

View file

@ -13,5 +13,15 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
return {
...baseIntegrationTestsConfig.getAll(),
testFiles: [require.resolve('.')],
kbnTestServer: {
...baseIntegrationTestsConfig.get('kbnTestServer'),
serverArgs: [
...baseIntegrationTestsConfig.get('kbnTestServer.serverArgs'),
'--xpack.upgrade_assistant.featureSet.mlSnapshots=true',
'--xpack.upgrade_assistant.featureSet.migrateSystemIndices=true',
'--xpack.upgrade_assistant.featureSet.migrateDataStreams=true',
'--xpack.upgrade_assistant.featureSet.reindexCorrectiveActions=true',
],
},
};
}

View file

@ -14,8 +14,7 @@ export default function ({ getService }: FtrProviderContext) {
const es = getService('es');
const supertest = getService('supertest');
// Failing: See https://github.com/elastic/kibana/issues/199719
describe.skip('Upgrade Assistant', function () {
describe('Upgrade Assistant', function () {
describe('Reindex operation saved object', () => {
const dotKibanaIndex = '.kibana';
const fakeSavedObjectId = 'fakeSavedObjectId';