Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dot-kibana-split] Update ES archiver to support multiple SO indices #154882

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ export type {
SavedObjectsExportablePredicate,
} from './src/saved_objects_management';
export type { SavedObjectStatusMeta } from './src/saved_objects_status';
export { MAIN_SAVED_OBJECT_INDEX } from './src/saved_objects_index_pattern';
export {
MAIN_SAVED_OBJECT_INDEX,
TASK_MANAGER_SAVED_OBJECT_INDEX,
SavedObjectsIndexPatterns,
} from './src/saved_objects_index_pattern';
export type {
SavedObjectsType,
SavedObjectTypeExcludeFromUpgradeFilterHook,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,13 @@
* Side Public License, v 1.
*/

/**
* Collect and centralize the names of the different saved object indices.
* Note that all of them start with the '.kibana' prefix.
* There are multiple places in the code that these indices have the form .kibana*.
* However, beware that there are some system indices that have the same prefix
* but are NOT used to store saved objects, e.g.: .kibana_security_session_1
*/
export const MAIN_SAVED_OBJECT_INDEX = '.kibana';
export const TASK_MANAGER_SAVED_OBJECT_INDEX = `${MAIN_SAVED_OBJECT_INDEX}_task_manager`;
export const SavedObjectsIndexPatterns = [MAIN_SAVED_OBJECT_INDEX, TASK_MANAGER_SAVED_OBJECT_INDEX];
3 changes: 2 additions & 1 deletion packages/kbn-es-archiver/src/actions/empty_kibana_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { Client } from '@elastic/elasticsearch';
import { ToolingLog } from '@kbn/tooling-log';
import { KbnClient } from '@kbn/test';

import { SavedObjectsIndexPatterns } from '@kbn/core-saved-objects-server';
import { migrateKibanaIndex, createStats, cleanKibanaIndices } from '../lib';

export async function emptyKibanaIndexAction({
Expand All @@ -25,6 +26,6 @@ export async function emptyKibanaIndexAction({

await cleanKibanaIndices({ client, stats, log });
await migrateKibanaIndex(kbnClient);
stats.createdIndex('.kibana');
SavedObjectsIndexPatterns.forEach((indexPattern) => stats.createdIndex(indexPattern));
return stats.toJSON();
}
10 changes: 6 additions & 4 deletions packages/kbn-es-archiver/src/actions/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { REPO_ROOT } from '@kbn/repo-info';
import type { KbnClient } from '@kbn/test';
import type { Client } from '@elastic/elasticsearch';
import { createPromiseFromStreams, concatStreamProviders } from '@kbn/utils';
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { ES_CLIENT_HEADERS } from '../client_headers';

import {
Expand Down Expand Up @@ -104,14 +105,15 @@ export async function loadAction({
}
);

// If we affected the Kibana index, we need to ensure it's migrated...
if (Object.keys(result).some((k) => k.startsWith('.kibana'))) {
// If we affected saved objects indices, we need to ensure they are migrated...
if (Object.keys(result).some((k) => k.startsWith(MAIN_SAVED_OBJECT_INDEX))) {
await migrateKibanaIndex(kbnClient);
log.debug('[%s] Migrated Kibana index after loading Kibana data', name);

if (kibanaPluginIds.includes('spaces')) {
await createDefaultSpace({ client, index: '.kibana' });
log.debug('[%s] Ensured that default space exists in .kibana', name);
// WARNING affected by #104081. Assumes 'spaces' saved objects are stored in MAIN_SAVED_OBJECT_INDEX
await createDefaultSpace({ client, index: MAIN_SAVED_OBJECT_INDEX });
log.debug(`[%s] Ensured that default space exists in ${MAIN_SAVED_OBJECT_INDEX}`, name);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import { Transform } from 'stream';
import type { Client } from '@elastic/elasticsearch';
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { Stats } from '../stats';
import { Progress } from '../progress';
import { ES_CLIENT_HEADERS } from '../../client_headers';
Expand Down Expand Up @@ -78,7 +79,9 @@ export function createGenerateDocRecordsStream({
// if keepIndexNames is false, rewrite the .kibana_* index to .kibana_1 so that
// when it is loaded it can skip migration, if possible
index:
hit._index.startsWith('.kibana') && !keepIndexNames ? '.kibana_1' : hit._index,
hit._index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !keepIndexNames
? `${MAIN_SAVED_OBJECT_INDEX}_1`
: hit._index,
data_stream: dataStream,
id: hit._id,
source: hit._source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import type { Client } from '@elastic/elasticsearch';
import { ToolingLog } from '@kbn/tooling-log';

import { IndicesPutIndexTemplateRequest } from '@elastic/elasticsearch/lib/api/types';
import {
MAIN_SAVED_OBJECT_INDEX,
TASK_MANAGER_SAVED_OBJECT_INDEX,
} from '@kbn/core-saved-objects-server';
import { Stats } from '../stats';
import { deleteKibanaIndices } from './kibana_index';
import { deleteIndex } from './delete_index';
Expand Down Expand Up @@ -96,8 +100,8 @@ export function createCreateIndexStream({

async function handleIndex(record: DocRecord) {
const { index, settings, mappings, aliases } = record.value;
const isKibanaTaskManager = index.startsWith('.kibana_task_manager');
const isKibana = index.startsWith('.kibana') && !isKibanaTaskManager;
const isKibanaTaskManager = index.startsWith(TASK_MANAGER_SAVED_OBJECT_INDEX);
const isKibana = index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !isKibanaTaskManager;

if (docsOnly) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Transform } from 'stream';
import type { Client } from '@elastic/elasticsearch';
import { ToolingLog } from '@kbn/tooling-log';

import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { Stats } from '../stats';
import { deleteIndex } from './delete_index';
import { cleanKibanaIndices } from './kibana_index';
Expand All @@ -28,7 +29,7 @@ export function createDeleteIndexStream(client: Client, stats: Stats, log: Tooli
if (record.type === 'index') {
const { index } = record.value;

if (index.startsWith('.kibana')) {
if (index.startsWith(MAIN_SAVED_OBJECT_INDEX)) {
await cleanKibanaIndices({ client, stats, log });
} else {
await deleteIndex({ client, stats, log, index });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import type { Client } from '@elastic/elasticsearch';
import { Transform } from 'stream';
import { ToolingLog } from '@kbn/tooling-log';
import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { Stats } from '../stats';
import { ES_CLIENT_HEADERS } from '../../client_headers';
import { getIndexTemplate } from '..';
Expand Down Expand Up @@ -100,7 +101,10 @@ export function createGenerateIndexRecordsStream({
value: {
// if keepIndexNames is false, rewrite the .kibana_* index to .kibana_1 so that
// when it is loaded it can skip migration, if possible
index: index.startsWith('.kibana') && !keepIndexNames ? '.kibana_1' : index,
index:
index.startsWith(MAIN_SAVED_OBJECT_INDEX) && !keepIndexNames
? `${MAIN_SAVED_OBJECT_INDEX}_1`
: index,
settings,
mappings,
aliases,
Expand Down
45 changes: 29 additions & 16 deletions packages/kbn-es-archiver/src/lib/indices/kibana_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ import { inspect } from 'util';
import type { Client } from '@elastic/elasticsearch';
import { ToolingLog } from '@kbn/tooling-log';
import { KbnClient } from '@kbn/test';
import {
MAIN_SAVED_OBJECT_INDEX,
SavedObjectsIndexPatterns,
TASK_MANAGER_SAVED_OBJECT_INDEX,
} from '@kbn/core-saved-objects-server';
import { Stats } from '../stats';
import { deleteIndex } from './delete_index';
import { ES_CLIENT_HEADERS } from '../../client_headers';

/**
* Deletes all indices that start with `.kibana`, or if onlyTaskManager==true, all indices that start with `.kibana_task_manager`
* Deletes all saved object indices, or if onlyTaskManager==true, it deletes task_manager indices
*/
export async function deleteKibanaIndices({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT similarly, we could rename this to deleteSavedObjectIndices.

client,
Expand All @@ -29,8 +34,10 @@ export async function deleteKibanaIndices({
onlyTaskManager?: boolean;
log: ToolingLog;
}) {
const indexPattern = onlyTaskManager ? '.kibana_task_manager*' : '.kibana*';
const indexNames = await fetchKibanaIndices(client, indexPattern);
// WARNING note that we are deleting ALL .kibana* indices here, NOT only the saved object ones
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on INDICES_REGEXP this seems to no longer be true

const indexNames = (await fetchKibanaIndices(client)).filter(
(indexName) => !onlyTaskManager || indexName.includes(TASK_MANAGER_SAVED_OBJECT_INDEX)
);
if (!indexNames.length) {
return;
}
Expand Down Expand Up @@ -65,22 +72,28 @@ export async function migrateKibanaIndex(kbnClient: KbnClient) {
}

/**
* Migrations mean that the Kibana index will look something like:
* .kibana, .kibana_1, .kibana_323, etc. This finds all indices starting
* with .kibana, then filters out any that aren't actually Kibana's core
* index (e.g. we don't want to remove .kibana_task_manager or the like).
* Check if the given index is a Kibana saved object index.
* This includes most .kibana_*
* but we must make sure that indices such as '.kibana_security_session_1' are NOT deleted.
*
* IMPORTANT
* Note that we can have more than 2 system indices (different SO types can go to different indices)
* ATM we have '.kibana', '.kibana_task_manager', '.kibana_cases'
* This method also takes into account legacy indices: .kibana_1, .kibana_task_manager_1.
* @param [index] the name of the index to check
* @returns boolean 'true' if the index is a Kibana saved object index.
*/

const LEGACY_INDICES_REGEXP = new RegExp(`^(${SavedObjectsIndexPatterns.join('|')})(:?_\\d*)?$`);
const INDICES_REGEXP = new RegExp(`^(${SavedObjectsIndexPatterns.join('|')})_(pre)?\\d+.\\d+.\\d+`);

function isKibanaIndex(index?: string): index is string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we call this isSavedObjectIndex?

return Boolean(
index &&
(/^\.kibana(:?_\d*)?$/.test(index) ||
/^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index))
);
return Boolean(index && (LEGACY_INDICES_REGEXP.test(index) || INDICES_REGEXP.test(index)));
}

async function fetchKibanaIndices(client: Client, indexPattern: string) {
async function fetchKibanaIndices(client: Client) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we rename to fetchSavedObjectIndices ?

const resp = await client.cat.indices(
{ index: indexPattern, format: 'json' },
{ index: `${MAIN_SAVED_OBJECT_INDEX}*`, format: 'json' },
{
headers: ES_CLIENT_HEADERS,
}
Expand All @@ -107,7 +120,7 @@ export async function cleanKibanaIndices({
while (true) {
const resp = await client.deleteByQuery(
{
index: `.kibana,.kibana_task_manager`,
index: SavedObjectsIndexPatterns,
body: {
query: {
bool: {
Expand Down Expand Up @@ -144,7 +157,7 @@ export async function cleanKibanaIndices({
`.kibana rather than deleting the whole index`
);

stats.deletedIndex('.kibana');
SavedObjectsIndexPatterns.forEach((indexPattern) => stats.deletedIndex(indexPattern));
}

export async function createDefaultSpace({ index, client }: { index: string; client: Client }) {
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-es-archiver/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"**/*.ts"
],
"kbn_references": [
"@kbn/core-saved-objects-server",
"@kbn/dev-utils",
"@kbn/test",
"@kbn/tooling-log",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
* Side Public License, v 1.
*/

import { MAIN_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import type { ProvidedType } from '@kbn/test';

import type { EsArchiverProvider } from '../es_archiver';
import type { RetryService } from '../retry';
import type { KibanaServerProvider } from './kibana_server';

const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload', 'emptyKibanaIndex'] as const;
const KIBANA_INDEX = '.kibana';

interface Options {
esArchiver: ProvidedType<typeof EsArchiverProvider>;
Expand All @@ -38,7 +38,7 @@ export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }:
const statsKeys = Object.keys(stats);
const kibanaKeys = statsKeys.filter(
// this also matches stats keys like '.kibana_1' and '.kibana_2,.kibana_1'
(key) => key.includes(KIBANA_INDEX) && stats[key].created
(key) => key.includes(MAIN_SAVED_OBJECT_INDEX) && stats[key].created
);

// if the kibana index was created by the esArchiver then update the uiSettings
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-ftr-common-functional-services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"**/*.ts",
],
"kbn_references": [
"@kbn/core-saved-objects-server",
"@kbn/tooling-log",
"@kbn/es-archiver",
"@kbn/test"
Expand Down