[8.6] Do not skip UPDATE_TARGET_MAPPINGS if upgrading to a newer stack version (#147503) (#147522)

# Backport

This will backport the following commits from `main` to `8.6`:
- [Do not skip UPDATE_TARGET_MAPPINGS if upgrading to a newer stack
version (#147503)](https://github.com/elastic/kibana/pull/147503)

<!--- Backport version: 8.9.7 -->

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

<!--BACKPORT [{"author":{"name":"Gerard
Soldevila","email":"gerard.soldevila@elastic.co"},"sourceCommit":{"committedDate":"2022-12-14T13:39:47Z","message":"Do
not skip UPDATE_TARGET_MAPPINGS if upgrading to a newer stack version
(#147503)\n\nFixes
https://github.com/elastic/kibana/issues/147450","sha":"4f72a2eb5beded9a3086de7766021d9b65a197f3","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","Team:Core","release_note:skip","Feature:Migrations","backport:prev-minor","v8.6.0","v8.7.0","v8.6.1"],"number":147503,"url":"https://github.com/elastic/kibana/pull/147503","mergeCommit":{"message":"Do
not skip UPDATE_TARGET_MAPPINGS if upgrading to a newer stack version
(#147503)\n\nFixes
https://github.com/elastic/kibana/issues/147450","sha":"4f72a2eb5beded9a3086de7766021d9b65a197f3"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/147503","number":147503,"mergeCommit":{"message":"Do
not skip UPDATE_TARGET_MAPPINGS if upgrading to a newer stack version
(#147503)\n\nFixes
https://github.com/elastic/kibana/issues/147450","sha":"4f72a2eb5beded9a3086de7766021d9b65a197f3"}}]}]
BACKPORT-->

Co-authored-by: Gerard Soldevila <gerard.soldevila@elastic.co>
This commit is contained in:
Kibana Machine 2022-12-14 09:45:35 -05:00 committed by GitHub
parent e4711bb3eb
commit c9ca8486c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 182 additions and 104 deletions

View file

@ -15,13 +15,13 @@ jest.mock('../core/build_active_mappings');
const diffMappingsMock = diffMappings as jest.MockedFn<typeof diffMappings>;
const sourceIndexMappings: IndexMapping = {
const actualMappings: IndexMapping = {
properties: {
field: { type: 'integer' },
},
};
const targetIndexMappings: IndexMapping = {
const expectedMappings: IndexMapping = {
properties: {
field: { type: 'long' },
},
@ -34,7 +34,7 @@ describe('checkTargetMappings', () => {
it('returns match=false if source mappings are not defined', async () => {
const task = checkTargetMappings({
targetIndexMappings,
expectedMappings,
});
const result = await task();
@ -44,21 +44,21 @@ describe('checkTargetMappings', () => {
it('calls diffMappings() with the source and target mappings', async () => {
const task = checkTargetMappings({
sourceIndexMappings,
targetIndexMappings,
actualMappings,
expectedMappings,
});
await task();
expect(diffMappings).toHaveBeenCalledTimes(1);
expect(diffMappings).toHaveBeenCalledWith(sourceIndexMappings, targetIndexMappings);
expect(diffMappings).toHaveBeenCalledWith(actualMappings, expectedMappings);
});
it('returns match=true if diffMappings() match', async () => {
diffMappingsMock.mockReturnValueOnce(undefined);
const task = checkTargetMappings({
sourceIndexMappings,
targetIndexMappings,
actualMappings,
expectedMappings,
});
const result = await task();
@ -69,8 +69,8 @@ describe('checkTargetMappings', () => {
diffMappingsMock.mockReturnValueOnce({ changedProp: 'field' });
const task = checkTargetMappings({
sourceIndexMappings,
targetIndexMappings,
actualMappings,
expectedMappings,
});
const result = await task();

View file

@ -13,8 +13,8 @@ import { diffMappings } from '../core/build_active_mappings';
/** @internal */
export interface CheckTargetMappingsParams {
sourceIndexMappings?: IndexMapping;
targetIndexMappings: IndexMapping;
actualMappings?: IndexMapping;
expectedMappings: IndexMapping;
}
/** @internal */
@ -24,13 +24,13 @@ export interface TargetMappingsCompareResult {
export const checkTargetMappings =
({
sourceIndexMappings,
targetIndexMappings,
actualMappings,
expectedMappings,
}: CheckTargetMappingsParams): TaskEither.TaskEither<never, TargetMappingsCompareResult> =>
async () => {
if (!sourceIndexMappings) {
if (!actualMappings) {
return Either.right({ match: false });
}
const diff = diffMappings(sourceIndexMappings, targetIndexMappings);
const diff = diffMappings(actualMappings, expectedMappings);
return Either.right({ match: !diff });
};

View file

@ -115,6 +115,10 @@ export const model = (currentState: State, resW: ResponseType<AllActionStates>):
sourceIndex: Option.none,
targetIndex: `${stateP.indexPrefix}_${stateP.kibanaVersion}_001`,
sourceIndexMappings: indices[source].mappings,
// in this scenario, a .kibana_X.Y.Z_001 index exists that matches the current kibana version
// aka we are NOT upgrading to a newer version
// we inject the target index's current mappings in the state, to check them later
targetIndexCurrentMappings: indices[source].mappings,
targetIndexMappings: mergeMigrationMappingPropertyHashes(
stateP.targetIndexMappings,
indices[aliases[stateP.currentAlias]!].mappings

View file

@ -132,8 +132,8 @@ export const nextActionMap = (client: ElasticsearchClient, transformRawDocs: Tra
Actions.refreshIndex({ client, targetIndex: state.targetIndex }),
CHECK_TARGET_MAPPINGS: (state: CheckTargetMappingsState) =>
Actions.checkTargetMappings({
sourceIndexMappings: state.sourceIndexMappings,
targetIndexMappings: state.targetIndexMappings,
actualMappings: state.targetIndexCurrentMappings,
expectedMappings: state.targetIndexMappings,
}),
UPDATE_TARGET_MAPPINGS: (state: UpdateTargetMappingsState) =>
Actions.updateAndPickupMappings({

View file

@ -173,6 +173,7 @@ export interface PostInitState extends BaseState {
readonly sourceIndex: Option.Option<string>;
/** The target index is the index to which the migration writes */
readonly targetIndex: string;
readonly targetIndexCurrentMappings?: IndexMapping;
readonly versionIndexReadyActions: Option.Option<AliasAction[]>;
readonly outdatedDocumentsQuery: QueryDslQueryContainer;
}

View file

@ -8,6 +8,7 @@
import Path from 'path';
import fs from 'fs/promises';
import { SemVer } from 'semver';
import JSON5 from 'json5';
import { Env } from '@kbn/config';
import { REPO_ROOT } from '@kbn/utils';
@ -77,88 +78,141 @@ describe('migration v2 - CHECK_TARGET_MAPPINGS', () => {
expect(logIncludes(logs, 'CHECK_TARGET_MAPPINGS')).toEqual(false);
});
it('skips UPDATE_TARGET_MAPPINGS for up-to-date deployments, when there are no changes in the mappings', async () => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
describe('when the indices are aligned with the stack version', () => {
it('skips UPDATE_TARGET_MAPPINGS if there are no changes in the mappings', async () => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
},
},
},
});
esServer = await startES();
// start Kibana a first time to create the system indices
root = createRoot();
await root.preboot();
await root.setup();
await root.start();
// stop Kibana and remove logs
await root.shutdown();
await delay(10);
await removeLogFile();
root = createRoot();
await root.preboot();
await root.setup();
await root.start();
// Check for migration steps present in the logs
logs = await parseLogFile();
expect(logIncludes(logs, 'CREATE_NEW_TARGET')).toEqual(false);
expect(
logIncludes(logs, 'CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS')
).toEqual(true);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS')).toEqual(false);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK')).toEqual(false);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_META')).toEqual(false);
});
esServer = await startES();
// start Kibana a first time to create the system indices
root = createRoot();
await root.preboot();
await root.setup();
await root.start();
// stop Kibana and remove logs
await root.shutdown();
await delay(10);
await removeLogFile();
root = createRoot();
await root.preboot();
await root.setup();
await root.start();
// Check for migration steps present in the logs
logs = await parseLogFile();
expect(logIncludes(logs, 'CREATE_NEW_TARGET')).toEqual(false);
expect(logIncludes(logs, 'CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS')).toEqual(
true
);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS')).toEqual(false);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK')).toEqual(false);
expect(logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_META')).toEqual(false);
});
it('runs UPDATE_TARGET_MAPPINGS when mappings have changed', async () => {
describe('when upgrading to a newer stack version', () => {
const currentVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
dataArchive: Path.join(__dirname, 'archives', '8.4.0_with_sample_data_logs.zip'),
it('runs UPDATE_TARGET_MAPPINGS when mappings have changed', async () => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
dataArchive: Path.join(__dirname, 'archives', '8.4.0_with_sample_data_logs.zip'),
},
},
},
});
esServer = await startES();
// start Kibana a first time to create the system indices
root = createRoot(currentVersion); // we discard a bunch of SO that have become unknown since 8.4.0
await root.preboot();
await root.setup();
await root.start();
// Check for migration steps present in the logs
logs = await parseLogFile();
expect(logIncludes(logs, 'CREATE_NEW_TARGET')).toEqual(false);
expect(logIncludes(logs, 'CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS')).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK -> UPDATE_TARGET_MAPPINGS_META')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS')
).toEqual(true);
expect(
logIncludes(logs, 'CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY')
).toEqual(true);
expect(logIncludes(logs, 'MARK_VERSION_INDEX_READY -> DONE')).toEqual(true);
expect(logIncludes(logs, 'Migration completed')).toEqual(true);
});
esServer = await startES();
it('runs UPDATE_TARGET_MAPPINGS even if the mappings have NOT changed', async () => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
},
},
});
// start Kibana a first time to create the system indices
root = createRoot(currentVersion); // we discard a bunch of SO that have become unknown since 8.4.0
await root.preboot();
await root.setup();
await root.start();
esServer = await startES();
// Check for migration steps present in the logs
logs = await parseLogFile();
expect(logIncludes(logs, 'CREATE_NEW_TARGET')).toEqual(false);
expect(logIncludes(logs, 'CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS')).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK -> UPDATE_TARGET_MAPPINGS_META')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS')
).toEqual(true);
expect(
logIncludes(logs, 'CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY')
).toEqual(true);
expect(logIncludes(logs, 'MARK_VERSION_INDEX_READY -> DONE')).toEqual(true);
expect(logIncludes(logs, 'Migration completed')).toEqual(true);
// start Kibana a first time to create the system indices
root = createRoot();
await root.preboot();
await root.setup();
await root.start();
// stop Kibana and remove logs
await root.shutdown();
await delay(10);
await removeLogFile();
const nextMinor = new SemVer(currentVersion).inc('patch').format();
root = createRoot(undefined, nextMinor);
await root.preboot();
await root.setup();
await root.start();
// Check for migration steps present in the logs
logs = await parseLogFile();
expect(logIncludes(logs, 'CREATE_NEW_TARGET')).toEqual(false);
expect(logIncludes(logs, 'CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS')).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_WAIT_FOR_TASK -> UPDATE_TARGET_MAPPINGS_META')
).toEqual(true);
expect(
logIncludes(logs, 'UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS')
).toEqual(true);
expect(
logIncludes(logs, 'CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY')
).toEqual(true);
expect(logIncludes(logs, 'MARK_VERSION_INDEX_READY -> DONE')).toEqual(true);
expect(logIncludes(logs, 'Migration completed')).toEqual(true);
});
});
});
function createRoot(discardUnknownObjects?: string) {
function createRoot(discardUnknownObjects?: string, customKibanaVersion?: string) {
return createRootWithCorePlugins(
{
migrations: {
@ -185,6 +239,7 @@ function createRoot(discardUnknownObjects?: string) {
},
{
oss: true,
}
},
customKibanaVersion
);
}

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
import { join } from 'path';
import loadJsonFile from 'load-json-file';
import { defaultsDeep } from 'lodash';
import { BehaviorSubject } from 'rxjs';
import supertest from 'supertest';
@ -19,7 +21,7 @@ import {
kibanaServerTestUser,
systemIndicesSuperuser,
} from '@kbn/test';
import { CliArgs, Env } from '@kbn/config';
import { CliArgs, Env, RawPackageInfo } from '@kbn/config';
import type { InternalCoreSetup, InternalCoreStart } from '@kbn/core-lifecycle-server-internal';
import { Root } from '@kbn/core-root-server-internal';
@ -45,22 +47,33 @@ const DEFAULTS_SETTINGS = {
export function createRootWithSettings(
settings: Record<string, any>,
cliArgs: Partial<CliArgs> = {}
cliArgs: Partial<CliArgs> = {},
customKibanaVersion?: string
) {
const env = Env.createDefault(REPO_ROOT, {
configs: [],
cliArgs: {
dev: false,
watch: false,
basePath: false,
runExamples: false,
oss: true,
disableOptimizer: true,
cache: true,
dist: false,
...cliArgs,
let pkg: RawPackageInfo | undefined;
if (customKibanaVersion) {
pkg = loadJsonFile.sync(join(REPO_ROOT, 'package.json')) as RawPackageInfo;
pkg.version = customKibanaVersion;
}
const env = Env.createDefault(
REPO_ROOT,
{
configs: [],
cliArgs: {
dev: false,
watch: false,
basePath: false,
runExamples: false,
oss: true,
disableOptimizer: true,
cache: true,
dist: false,
...cliArgs,
},
},
});
pkg
);
return new Root(
{
@ -103,7 +116,11 @@ export function createRoot(settings = {}, cliArgs: Partial<CliArgs> = {}) {
* @param {Object} [settings={}] Any config overrides for this instance.
* @returns {Root}
*/
export function createRootWithCorePlugins(settings = {}, cliArgs: Partial<CliArgs> = {}) {
export function createRootWithCorePlugins(
settings = {},
cliArgs: Partial<CliArgs> = {},
customKibanaVersion?: string
) {
const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = {
elasticsearch: {
hosts: [esTestConfig.getUrl()],
@ -144,7 +161,8 @@ export function createRootWithCorePlugins(settings = {}, cliArgs: Partial<CliArg
return createRootWithSettings(
defaultsDeep({}, settings, DEFAULT_SETTINGS_WITH_CORE_PLUGINS),
cliArgs
cliArgs,
customKibanaVersion
);
}