[8.7] [Kibana migrator test kit] Flush logger after running migrations (#152818) (#152890)

# Backport

This will backport the following commits from `main` to `8.7`:
- [[Kibana migrator test kit] Flush logger after running migrations
(#152818)](https://github.com/elastic/kibana/pull/152818)

<!--- 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":"2023-03-07T17:57:17Z","message":"[Kibana
migrator test kit] Flush logger after running migrations
(#152818)\n\nThis PR addresses flakiness of:\r\n\r\n-
https://github.com/elastic/kibana/issues/152472\r\n-
https://github.com/elastic/kibana/issues/152448\r\n\r\nThese tests fail
because we have a race condition. We are checking\r\ncertain conditions
in the log files, and sometimes we do it before the\r\nlogs are actually
written.\r\n\r\nWith the Kibana migrator test kit, we can actually flush
+ wait for all\r\nthe logging appenders. This way, we are sure that the
logs are complete\r\nwhen we inspect them in the
tests.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"163dae7154c11d92ff148bc6ac45cbea54680769","branchLabelMapping":{"^v8.8.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","Team:Core","release_note:skip","test-failure-flaky","Feature:Migrations","backport:prev-minor","v8.7.0","v8.8.0"],"number":152818,"url":"https://github.com/elastic/kibana/pull/152818","mergeCommit":{"message":"[Kibana
migrator test kit] Flush logger after running migrations
(#152818)\n\nThis PR addresses flakiness of:\r\n\r\n-
https://github.com/elastic/kibana/issues/152472\r\n-
https://github.com/elastic/kibana/issues/152448\r\n\r\nThese tests fail
because we have a race condition. We are checking\r\ncertain conditions
in the log files, and sometimes we do it before the\r\nlogs are actually
written.\r\n\r\nWith the Kibana migrator test kit, we can actually flush
+ wait for all\r\nthe logging appenders. This way, we are sure that the
logs are complete\r\nwhen we inspect them in the
tests.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"163dae7154c11d92ff148bc6ac45cbea54680769"}},"sourceBranch":"main","suggestedTargetBranches":["8.7"],"targetPullRequestStates":[{"branch":"8.7","label":"v8.7.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.8.0","labelRegex":"^v8.8.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/152818","number":152818,"mergeCommit":{"message":"[Kibana
migrator test kit] Flush logger after running migrations
(#152818)\n\nThis PR addresses flakiness of:\r\n\r\n-
https://github.com/elastic/kibana/issues/152472\r\n-
https://github.com/elastic/kibana/issues/152448\r\n\r\nThese tests fail
because we have a race condition. We are checking\r\ncertain conditions
in the log files, and sometimes we do it before the\r\nlogs are actually
written.\r\n\r\nWith the Kibana migrator test kit, we can actually flush
+ wait for all\r\nthe logging appenders. This way, we are sure that the
logs are complete\r\nwhen we inspect them in the
tests.\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"163dae7154c11d92ff148bc6ac45cbea54680769"}}]}]
BACKPORT-->
This commit is contained in:
Gerard Soldevila 2023-03-08 13:04:04 +01:00 committed by GitHub
parent db3b8c3108
commit e70452f969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 462 additions and 233 deletions

View file

@ -10,28 +10,174 @@ import Path from 'path';
import Fs from 'fs';
import Util from 'util';
import JSON5 from 'json5';
import { type TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server';
import { SavedObjectsType } from '@kbn/core-saved-objects-server';
import { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { getMigrationDocLink, delay } from '../test_utils';
import {
createTestServers,
createRootWithCorePlugins,
type TestElasticsearchUtils,
} from '@kbn/core-test-helpers-kbn-server';
import { Root } from '@kbn/core-root-server-internal';
import { getMigrationDocLink } from '../test_utils';
clearLog,
currentVersion,
defaultKibanaIndex,
getKibanaMigratorTestKit,
nextMinor,
startElasticsearch,
} from '../kibana_migrator_test_kit';
const migrationDocLink = getMigrationDocLink().resolveMigrationFailures;
const logFilePath = Path.join(__dirname, 'cleanup.log');
const asyncUnlink = Util.promisify(Fs.unlink);
const asyncReadFile = Util.promisify(Fs.readFile);
async function removeLogFile() {
// ignore errors if it doesn't exist
await asyncUnlink(logFilePath).catch(() => void 0);
}
describe('migration v2', () => {
let esServer: TestElasticsearchUtils['es'];
let esClient: ElasticsearchClient;
function createRoot() {
return createRootWithCorePlugins(
beforeAll(async () => {
esServer = await startElasticsearch();
});
beforeEach(async () => {
esClient = await setupBaseline();
await clearLog(logFilePath);
});
it('clean ups if migration fails', async () => {
const { runMigrations, client } = await setupNextMinor();
await expect(runMigrations()).rejects.toThrowErrorMatchingInlineSnapshot(`
"Unable to complete saved object migrations for the [${defaultKibanaIndex}] index: Migrations failed. Reason: 1 corrupt saved object documents were found: corrupt:2baf4de0-a6d4-11ed-ba5a-39196fc76e60
To allow migrations to proceed, please delete or fix these documents.
Note that you can configure Kibana to automatically discard corrupt documents and transform errors for this migration.
Please refer to ${migrationDocLink} for more information."
`);
const logFileContent = await asyncReadFile(logFilePath, 'utf-8');
const records = logFileContent
.split('\n')
.filter(Boolean)
.map((str) => JSON5.parse(str));
const logRecordWithPit = records.find(
(rec) => rec.message === `[${defaultKibanaIndex}] REINDEX_SOURCE_TO_TEMP_OPEN_PIT RESPONSE`
);
expect(logRecordWithPit).toBeTruthy();
const pitId = logRecordWithPit.right.pitId;
expect(pitId).toBeTruthy();
await expect(
client.search({
body: {
pit: { id: pitId },
},
})
// throws an exception that cannot search with closed PIT
).rejects.toThrow(/search_phase_execution_exception/);
});
afterEach(async () => {
await esClient?.indices.delete({ index: `${defaultKibanaIndex}_${currentVersion}_001` });
});
afterAll(async () => {
await esServer?.stop();
await delay(10);
});
});
const setupBaseline = async () => {
const typesCurrent: SavedObjectsType[] = [
{
name: 'complex',
hidden: false,
namespaceType: 'agnostic',
mappings: {
properties: {
name: { type: 'text' },
value: { type: 'integer' },
},
},
migrations: {},
},
];
const savedObjects = [
{
id: 'complex:4baf4de0-a6d4-11ed-ba5a-39196fc76e60',
body: {
type: 'complex',
complex: {
name: 'foo',
value: 5,
},
references: [],
coreMigrationVersion: currentVersion,
updated_at: '2023-02-07T11:04:44.914Z',
created_at: '2023-02-07T11:04:44.914Z',
},
},
{
id: 'corrupt:2baf4de0-a6d4-11ed-ba5a-39196fc76e60', // incorrect id => corrupt object
body: {
type: 'complex',
complex: {
name: 'bar',
value: 3,
},
references: [],
coreMigrationVersion: currentVersion,
updated_at: '2023-02-07T11:04:44.914Z',
created_at: '2023-02-07T11:04:44.914Z',
},
},
];
const { runMigrations, client } = await getKibanaMigratorTestKit({
types: typesCurrent,
logFilePath,
});
await runMigrations();
// inject corrupt saved objects directly using esClient
await Promise.all(
savedObjects.map((savedObject) => {
client.create({
index: defaultKibanaIndex,
refresh: 'wait_for',
...savedObject,
});
})
);
return client;
};
const setupNextMinor = async () => {
const typesNextMinor: SavedObjectsType[] = [
{
name: 'complex',
hidden: false,
namespaceType: 'agnostic',
mappings: {
properties: {
name: { type: 'keyword' },
value: { type: 'long' },
},
},
migrations: {
[nextMinor]: (doc) => doc,
},
},
];
return await getKibanaMigratorTestKit({
types: typesNextMinor,
kibanaVersion: nextMinor,
logFilePath,
settings: {
migrations: {
skip: false,
},
@ -54,102 +200,5 @@ function createRoot() {
],
},
},
{
oss: true,
}
);
}
describe('migration v2', () => {
let esServer: TestElasticsearchUtils;
let root: Root;
beforeAll(async () => {
await removeLogFile();
});
afterAll(async () => {
if (root) {
await root.shutdown();
}
if (esServer) {
await esServer.stop();
}
await new Promise((resolve) => setTimeout(resolve, 10000));
});
it('clean ups if migration fails', async () => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
// original SO:
// {
// _index: '.kibana_7.13.0_001',
// _type: '_doc',
// _id: 'index-pattern:test_index*',
// _version: 1,
// result: 'created',
// _shards: { total: 2, successful: 1, failed: 0 },
// _seq_no: 0,
// _primary_term: 1
// }
dataArchive: Path.join(__dirname, '..', 'archives', '7.13.0_with_corrupted_so.zip'),
},
},
});
root = createRoot();
esServer = await startES();
await root.preboot();
const coreSetup = await root.setup();
coreSetup.savedObjects.registerType({
name: 'foo',
hidden: false,
mappings: {
properties: {},
},
namespaceType: 'agnostic',
migrations: {
'7.14.0': (doc) => doc,
},
});
await expect(root.start()).rejects.toThrowErrorMatchingInlineSnapshot(`
"Unable to complete saved object migrations for the [.kibana] index: Migrations failed. Reason: 1 corrupt saved object documents were found: index-pattern:test_index*
To allow migrations to proceed, please delete or fix these documents.
Note that you can configure Kibana to automatically discard corrupt documents and transform errors for this migration.
Please refer to ${migrationDocLink} for more information."
`);
const logFileContent = await asyncReadFile(logFilePath, 'utf-8');
const records = logFileContent
.split('\n')
.filter(Boolean)
.map((str) => JSON5.parse(str));
const logRecordWithPit = records.find(
(rec) => rec.message === '[.kibana] REINDEX_SOURCE_TO_TEMP_OPEN_PIT RESPONSE'
);
expect(logRecordWithPit).toBeTruthy();
const pitId = logRecordWithPit.right.pitId;
expect(pitId).toBeTruthy();
const client = esServer.es.getClient();
await expect(
client.search({
body: {
pit: { id: pitId },
},
})
// throws an exception that cannot search with closed PIT
).rejects.toThrow(/search_phase_execution_exception/);
});
});
};

View file

@ -5,121 +5,105 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import Path from 'path';
import fs from 'fs/promises';
import { Env } from '@kbn/config';
import { getEnvOptions } from '@kbn/config-mocks';
import { REPO_ROOT } from '@kbn/repo-info';
import type { Root } from '@kbn/core-root-server-internal';
import type { TestElasticsearchUtils } from '@kbn/core-test-helpers-kbn-server';
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import type { MigrationResult } from '@kbn/core-saved-objects-base-server-internal';
import {
createRootWithCorePlugins,
createTestServers,
type TestElasticsearchUtils,
} from '@kbn/core-test-helpers-kbn-server';
readLog,
clearLog,
createBaseline,
currentVersion,
defaultKibanaIndex,
getIdenticalMappingsMigrator,
getIncompatibleMappingsMigrator,
startElasticsearch,
} from '../kibana_migrator_test_kit';
import { delay } from '../test_utils';
import { SemVer } from 'semver';
const logFilePath = Path.join(__dirname, 'skip_reindex.log');
describe('skip reindexing', () => {
const currentVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
describe('when migrating to a new version', () => {
let esServer: TestElasticsearchUtils['es'];
let root: Root;
let esClient: ElasticsearchClient;
let runMigrations: (rerun?: boolean | undefined) => Promise<MigrationResult[]>;
beforeAll(async () => {
esServer = await startElasticsearch();
});
beforeEach(async () => {
esClient = await createBaseline();
await clearLog();
});
describe('and the mappings remain the same', () => {
it('the migrator skips reindexing', async () => {
// we run the migrator with the same identic baseline types
runMigrations = (await getIdenticalMappingsMigrator()).runMigrations;
await runMigrations();
const logs = await readLog();
expect(logs).toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE.');
expect(logs).toMatch('WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED.');
expect(logs).toMatch(
'CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK.'
);
expect(logs).toMatch(
'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION.'
);
expect(logs).toMatch('PREPARE_COMPATIBLE_MIGRATION -> REFRESH_TARGET.');
expect(logs).toMatch('REFRESH_TARGET -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT.');
expect(logs).toMatch('CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS.');
expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> DONE.');
expect(logs).not.toMatch('CREATE_NEW_TARGET');
expect(logs).not.toMatch('CHECK_UNKNOWN_DOCUMENTS');
expect(logs).not.toMatch('REINDEX');
expect(logs).not.toMatch('UPDATE_TARGET_MAPPINGS_PROPERTIES');
});
});
describe('and the mappings have changed', () => {
it('the migrator reindexes documents to a new index', async () => {
// we run the migrator with altered, compatible mappings
runMigrations = (await getIncompatibleMappingsMigrator()).runMigrations;
await runMigrations();
const logs = await readLog();
expect(logs).toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE.');
expect(logs).toMatch('WAIT_FOR_YELLOW_SOURCE -> CHECK_UNKNOWN_DOCUMENTS.');
expect(logs).toMatch('CALCULATE_EXCLUDE_FILTERS -> CREATE_REINDEX_TEMP.');
expect(logs).toMatch('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS.');
expect(logs).toMatch('UPDATE_TARGET_MAPPINGS_META -> CHECK_VERSION_INDEX_READY_ACTIONS.');
expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> MARK_VERSION_INDEX_READY.');
expect(logs).toMatch('MARK_VERSION_INDEX_READY -> DONE.');
expect(logs).not.toMatch('CREATE_NEW_TARGET');
expect(logs).not.toMatch('CLEANUP_UNKNOWN_AND_EXCLUDED');
expect(logs).not.toMatch('PREPARE_COMPATIBLE_MIGRATION');
});
});
afterEach(async () => {
await root?.shutdown();
// we run the migrator again to ensure that the next time state is loaded everything still works as expected
await clearLog();
await runMigrations(true);
const logs = await readLog();
expect(logs).toMatch('INIT -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT.');
expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> DONE.');
expect(logs).not.toMatch('WAIT_FOR_YELLOW_SOURCE');
expect(logs).not.toMatch('CLEANUP_UNKNOWN_AND_EXCLUCED');
expect(logs).not.toMatch('CREATE_NEW_TARGET');
expect(logs).not.toMatch('PREPARE_COMPATIBLE_MIGRATION');
expect(logs).not.toMatch('UPDATE_TARGET_MAPPINGS_PROPERTIES');
// clear the system index for next test
await esClient?.indices.delete({ index: `${defaultKibanaIndex}_${currentVersion}_001` });
});
afterAll(async () => {
await esServer?.stop();
await delay(10);
});
it('when migrating to a new version, but mappings remain the same', async () => {
let logs: string;
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
es: {
license: 'basic',
},
},
});
esServer = await startES();
root = createRoot();
// Run initial migrations
await root.preboot();
await root.setup();
await root.start();
// stop Kibana and remove logs
await root.shutdown();
await delay(10);
await fs.unlink(logFilePath).catch(() => {});
const nextPatch = new SemVer(currentVersion).inc('patch').format();
root = createRoot(nextPatch);
await root.preboot();
await root.setup();
await root.start();
logs = await fs.readFile(logFilePath, 'utf-8');
expect(logs).toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE');
expect(logs).toMatch('WAIT_FOR_YELLOW_SOURCE -> CLEANUP_UNKNOWN_AND_EXCLUDED');
expect(logs).toMatch(
'CLEANUP_UNKNOWN_AND_EXCLUDED -> CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK'
);
expect(logs).toMatch(
'CLEANUP_UNKNOWN_AND_EXCLUDED_WAIT_FOR_TASK -> PREPARE_COMPATIBLE_MIGRATION'
);
expect(logs).toMatch('PREPARE_COMPATIBLE_MIGRATION -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT');
expect(logs).toMatch('CHECK_TARGET_MAPPINGS -> CHECK_VERSION_INDEX_READY_ACTIONS');
expect(logs).toMatch('CHECK_VERSION_INDEX_READY_ACTIONS -> DONE');
expect(logs).not.toMatch('CREATE_NEW_TARGET');
expect(logs).not.toMatch('CHECK_TARGET_MAPPINGS -> UPDATE_TARGET_MAPPINGS');
// We restart Kibana again after doing a "compatible migration" to ensure that
// the next time state is loaded everything still works as expected.
// For instance, we might see something like:
// Unable to complete saved object migrations for the [.kibana] index. Please check the health of your Elasticsearch cluster and try again. Unexpected Elasticsearch ResponseError: statusCode: 404, method: POST, url: /.kibana_8.7.1_001/_pit?keep_alive=10m error: [index_not_found_exception]: no such index [.kibana_8.7.1_001]
await root.shutdown();
await delay(10);
await fs.unlink(logFilePath).catch(() => {});
root = createRoot(nextPatch);
await root.preboot();
await root.setup();
await root.start();
logs = await fs.readFile(logFilePath, 'utf-8');
expect(logs).toMatch('INIT -> OUTDATED_DOCUMENTS_SEARCH_OPEN_PIT');
expect(logs).not.toMatch('INIT -> WAIT_FOR_YELLOW_SOURCE');
});
});
function createRoot(kibanaVersion?: string): Root {
return createRootWithCorePlugins(
{
logging: {
appenders: {
file: {
type: 'file',
fileName: logFilePath,
layout: {
type: 'json',
},
},
},
loggers: [
{
name: 'root',
level: 'info',
appenders: ['file'],
},
],
},
},
{ oss: true },
kibanaVersion
);
}

View file

@ -0,0 +1,82 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SavedObjectsBulkCreateObject } from '@kbn/core-saved-objects-api-server';
import type { SavedObjectsType } from '@kbn/core-saved-objects-server';
const defaultType: SavedObjectsType<any> = {
name: 'defaultType',
hidden: false,
namespaceType: 'agnostic',
mappings: {
properties: {
name: { type: 'keyword' },
},
},
migrations: {},
};
export const baselineTypes: Array<SavedObjectsType<any>> = [
{
...defaultType,
name: 'server',
},
{
...defaultType,
name: 'basic',
},
{
...defaultType,
name: 'deprecated',
},
{
...defaultType,
name: 'complex',
mappings: {
properties: {
name: { type: 'text' },
value: { type: 'integer' },
},
},
excludeOnUpgrade: () => {
return {
bool: {
must: [{ term: { type: 'complex' } }, { range: { 'complex.value': { lte: 1 } } }],
},
};
},
},
];
export const baselineDocuments: SavedObjectsBulkCreateObject[] = [
...['server-foo', 'server-bar', 'server-baz'].map((name) => ({
type: 'server',
attributes: {
name,
},
})),
...['basic-foo', 'basic-bar', 'basic-baz'].map((name) => ({
type: 'basic',
attributes: {
name,
},
})),
...['deprecated-foo', 'deprecated-bar', 'deprecated-baz'].map((name) => ({
type: 'deprecated',
attributes: {
name,
},
})),
...['complex-foo', 'complex-bar', 'complex-baz', 'complex-lipsum'].map((name, index) => ({
type: 'complex',
attributes: {
name,
value: index,
},
})),
];

View file

@ -7,6 +7,9 @@
*/
import Path from 'path';
import fs from 'fs/promises';
import { SemVer } from 'semver';
import { defaultsDeep } from 'lodash';
import { BehaviorSubject, firstValueFrom, map } from 'rxjs';
import { ConfigService, Env } from '@kbn/config';
@ -19,8 +22,7 @@ import {
type SavedObjectsConfigType,
type SavedObjectsMigrationConfigType,
SavedObjectTypeRegistry,
IKibanaMigrator,
MigrationResult,
type MigrationResult,
} from '@kbn/core-saved-objects-base-server-internal';
import { SavedObjectsRepository } from '@kbn/core-saved-objects-api-server-internal';
import {
@ -32,20 +34,23 @@ import { type LoggingConfigType, LoggingSystem } from '@kbn/core-logging-server-
import type { ISavedObjectTypeRegistry, SavedObjectsType } from '@kbn/core-saved-objects-server';
import { esTestConfig, kibanaServerTestUser } from '@kbn/test';
import { LoggerFactory } from '@kbn/logging';
import type { LoggerFactory } from '@kbn/logging';
import { createTestServers } from '@kbn/core-test-helpers-kbn-server';
import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
import { registerServiceConfig } from '@kbn/core-root-server-internal';
import { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
import { getDocLinks, getDocLinksMeta } from '@kbn/doc-links';
import { DocLinksServiceStart } from '@kbn/core-doc-links-server';
import { createTestServers } from '@kbn/core-test-helpers-kbn-server';
import type { DocLinksServiceStart } from '@kbn/core-doc-links-server';
import { baselineDocuments, baselineTypes } from './kibana_migrator_test_kit.fixtures';
export const defaultLogFilePath = Path.join(__dirname, 'kibana_migrator_test_kit.log');
const env = Env.createDefault(REPO_ROOT, getEnvOptions());
// Extract current stack version from Env, to use as a default
const currentVersion = env.packageInfo.version;
const currentBranch = env.packageInfo.branch;
export const currentVersion = env.packageInfo.version;
export const nextMinor = new SemVer(currentVersion).inc('minor').format();
export const currentBranch = env.packageInfo.branch;
export const defaultKibanaIndex = '.kibana_migrator_tests';
export interface GetEsClientParams {
settings?: Record<string, any>;
@ -64,7 +69,6 @@ export interface KibanaMigratorTestKitParams {
export interface KibanaMigratorTestKit {
client: ElasticsearchClient;
migrator: IKibanaMigrator;
runMigrations: (rerun?: boolean) => Promise<MigrationResult[]>;
typeRegistry: ISavedObjectTypeRegistry;
savedObjectsRepository: ISavedObjectsRepository;
@ -76,7 +80,7 @@ export const startElasticsearch = async ({
}: {
basePath?: string;
dataArchive?: string;
}) => {
} = {}) => {
const { startES } = createTestServers({
adjustTimeout: (t: number) => jest.setTimeout(t),
settings: {
@ -109,7 +113,7 @@ export const getEsClient = async ({
export const getKibanaMigratorTestKit = async ({
settings = {},
kibanaIndex = '.kibana',
kibanaIndex = defaultKibanaIndex,
kibanaVersion = currentVersion,
kibanaBranch = currentBranch,
types = [],
@ -122,7 +126,6 @@ export const getKibanaMigratorTestKit = async ({
// configure logging system
const loggingConf = await firstValueFrom(configService.atPath<LoggingConfigType>('logging'));
loggingSystem.upgrade(loggingConf);
const client = await getElasticsearchClient(configService, loggerFactory, kibanaVersion);
@ -142,8 +145,11 @@ export const getKibanaMigratorTestKit = async ({
);
const runMigrations = async (rerun?: boolean) => {
loggingSystem.upgrade(loggingConf);
migrator.prepareMigrations();
return await migrator.runMigrations({ rerun });
const migrationResults = await migrator.runMigrations({ rerun });
await loggingSystem.stop();
return migrationResults;
};
const savedObjectsRepository = SavedObjectsRepository.createRepository(
@ -156,7 +162,6 @@ export const getKibanaMigratorTestKit = async ({
return {
client,
migrator,
runMigrations,
typeRegistry,
savedObjectsRepository,
@ -272,3 +277,112 @@ const registerTypes = (
) => {
(types || []).forEach((type) => typeRegistry.registerType(type));
};
export const createBaseline = async () => {
const { client, runMigrations, savedObjectsRepository } = await getKibanaMigratorTestKit({
kibanaIndex: defaultKibanaIndex,
types: baselineTypes,
});
await runMigrations();
await savedObjectsRepository.bulkCreate(baselineDocuments, {
refresh: 'wait_for',
});
return client;
};
interface GetMutatedMigratorParams {
kibanaVersion?: string;
settings?: Record<string, any>;
}
export const getIdenticalMappingsMigrator = async ({
kibanaVersion = nextMinor,
settings = {},
}: GetMutatedMigratorParams = {}) => {
return await getKibanaMigratorTestKit({
types: baselineTypes,
kibanaVersion,
settings,
});
};
export const getNonDeprecatedMappingsMigrator = async ({
kibanaVersion = nextMinor,
settings = {},
}: GetMutatedMigratorParams = {}) => {
return await getKibanaMigratorTestKit({
types: baselineTypes.filter((type) => type.name !== 'deprecated'),
kibanaVersion,
settings,
});
};
export const getCompatibleMappingsMigrator = async ({
filterDeprecated = false,
kibanaVersion = nextMinor,
settings = {},
}: GetMutatedMigratorParams & { filterDeprecated?: boolean } = {}) => {
const types = baselineTypes
.filter((type) => !filterDeprecated || type.name !== 'deprecated')
.map<SavedObjectsType>((type) => {
if (type.name === 'complex') {
return {
...type,
mappings: {
properties: {
name: { type: 'text' },
value: { type: 'integer' },
createdAt: { type: 'date' },
},
},
};
} else {
return type;
}
});
return await getKibanaMigratorTestKit({
types,
kibanaVersion,
settings,
});
};
export const getIncompatibleMappingsMigrator = async ({
kibanaVersion = nextMinor,
settings = {},
}: GetMutatedMigratorParams = {}) => {
const types = baselineTypes.map<SavedObjectsType>((type) => {
if (type.name === 'complex') {
return {
...type,
mappings: {
properties: {
name: { type: 'keyword' },
value: { type: 'long' },
createdAt: { type: 'date' },
},
},
};
} else {
return type;
}
});
return await getKibanaMigratorTestKit({
types,
kibanaVersion,
settings,
});
};
export const readLog = async (logFilePath: string = defaultLogFilePath): Promise<string> => {
return await fs.readFile(logFilePath, 'utf-8');
};
export const clearLog = async (logFilePath: string = defaultLogFilePath): Promise<void> => {
await fs.truncate(logFilePath).catch(() => {});
};