Unrevert "Migrations v2: don't auto-create indices + FTR/esArchiver support (#85778)" (#89992)

* Revert "Revert "Migrations v2: don't auto-create indices + FTR/esArchiver support (#85778)""

This reverts commit f97958043f.

* Fix flaky saved objects management test #89953

* If a clone target exists, wait for yellow, not green, index status

* Fix test after master merge

* Fix types

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Rudolf Meijering 2021-02-06 18:45:20 +01:00 committed by GitHub
parent 826a1ecbdb
commit fd1d965039
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 709 additions and 324 deletions

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [createIndexAliasNotFoundError](./kibana-plugin-core-server.savedobjectserrorhelpers.createindexaliasnotfounderror.md)
## SavedObjectsErrorHelpers.createIndexAliasNotFoundError() method
<b>Signature:</b>
```typescript
static createIndexAliasNotFoundError(alias: string): DecoratedError;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| alias | <code>string</code> | |
<b>Returns:</b>
`DecoratedError`

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [decorateIndexAliasNotFoundError](./kibana-plugin-core-server.savedobjectserrorhelpers.decorateindexaliasnotfounderror.md)
## SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError() method
<b>Signature:</b>
```typescript
static decorateIndexAliasNotFoundError(error: Error, alias: string): DecoratedError;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| error | <code>Error</code> | |
| alias | <code>string</code> | |
<b>Returns:</b>
`DecoratedError`

View file

@ -0,0 +1,22 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [isGeneralError](./kibana-plugin-core-server.savedobjectserrorhelpers.isgeneralerror.md)
## SavedObjectsErrorHelpers.isGeneralError() method
<b>Signature:</b>
```typescript
static isGeneralError(error: Error | DecoratedError): boolean;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| error | <code>Error &#124; DecoratedError</code> | |
<b>Returns:</b>
`boolean`

View file

@ -18,6 +18,7 @@ export declare class SavedObjectsErrorHelpers
| [createBadRequestError(reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.createbadrequesterror.md) | <code>static</code> | |
| [createConflictError(type, id, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.createconflicterror.md) | <code>static</code> | |
| [createGenericNotFoundError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfounderror.md) | <code>static</code> | |
| [createIndexAliasNotFoundError(alias)](./kibana-plugin-core-server.savedobjectserrorhelpers.createindexaliasnotfounderror.md) | <code>static</code> | |
| [createInvalidVersionError(versionInput)](./kibana-plugin-core-server.savedobjectserrorhelpers.createinvalidversionerror.md) | <code>static</code> | |
| [createTooManyRequestsError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.createtoomanyrequestserror.md) | <code>static</code> | |
| [createUnsupportedTypeError(type)](./kibana-plugin-core-server.savedobjectserrorhelpers.createunsupportedtypeerror.md) | <code>static</code> | |
@ -27,6 +28,7 @@ export declare class SavedObjectsErrorHelpers
| [decorateEsUnavailableError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorateesunavailableerror.md) | <code>static</code> | |
| [decorateForbiddenError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorateforbiddenerror.md) | <code>static</code> | |
| [decorateGeneralError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorategeneralerror.md) | <code>static</code> | |
| [decorateIndexAliasNotFoundError(error, alias)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorateindexaliasnotfounderror.md) | <code>static</code> | |
| [decorateNotAuthorizedError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratenotauthorizederror.md) | <code>static</code> | |
| [decorateRequestEntityTooLargeError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoraterequestentitytoolargeerror.md) | <code>static</code> | |
| [decorateTooManyRequestsError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratetoomanyrequestserror.md) | <code>static</code> | |
@ -35,6 +37,7 @@ export declare class SavedObjectsErrorHelpers
| [isEsCannotExecuteScriptError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isescannotexecutescripterror.md) | <code>static</code> | |
| [isEsUnavailableError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isesunavailableerror.md) | <code>static</code> | |
| [isForbiddenError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isforbiddenerror.md) | <code>static</code> | |
| [isGeneralError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isgeneralerror.md) | <code>static</code> | |
| [isInvalidVersionError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isinvalidversionerror.md) | <code>static</code> | |
| [isNotAuthorizedError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isnotauthorizederror.md) | <code>static</code> | |
| [isNotFoundError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isnotfounderror.md) | <code>static</code> | |

View file

@ -25,5 +25,6 @@ export async function emptyKibanaIndexAction({
await cleanKibanaIndices({ client, stats, log, kibanaPluginIds });
await migrateKibanaIndex({ client, kbnClient });
return stats;
stats.createdIndex('.kibana');
return stats.toJSON();
}

View file

@ -155,7 +155,7 @@ export class EsArchiver {
* @return Promise
*/
async emptyKibanaIndex() {
await emptyKibanaIndexAction({
return await emptyKibanaIndexAction({
client: this.client,
log: this.log,
kbnClient: this.kbnClient,

View file

@ -82,7 +82,9 @@ export async function migrateKibanaIndex({
*/
async function fetchKibanaIndices(client: Client) {
const resp = await client.cat.indices<unknown>({ index: '.kibana*', format: 'json' });
const isKibanaIndex = (index: string) => /^\.kibana(:?_\d*)?$/.test(index);
const isKibanaIndex = (index: string) =>
/^\.kibana(:?_\d*)?$/.test(index) ||
/^\.kibana(_task_manager)?_(pre)?\d+\.\d+\.\d+/.test(index);
if (!Array.isArray(resp.body)) {
throw new Error(`expected response to be an array ${inspect(resp.body)}`);
@ -115,7 +117,7 @@ export async function cleanKibanaIndices({
while (true) {
const resp = await client.deleteByQuery(
{
index: `.kibana`,
index: `.kibana,.kibana_task_manager`,
body: {
query: {
bool: {
@ -129,7 +131,7 @@ export async function cleanKibanaIndices({
},
},
{
ignore: [409],
ignore: [404, 409],
}
);

View file

@ -150,12 +150,23 @@ export const removeWriteBlock = (
.catch(catchRetryableEsClientErrors);
};
const waitForIndexStatusGreen = (
/**
* A yellow index status means the index's primary shard is allocated and the
* index is ready for searching/indexing documents, but ES wasn't able to
* allocate the replicas. When migrations proceed with a yellow index it means
* we don't have as much data-redundancy as we could have, but waiting for
* replicas would mean that v2 migrations fail where v1 migrations would have
* succeeded. It doesn't feel like it's Kibana's job to force users to keep
* their clusters green and even if it's green when we migrate it can turn
* yellow at any point in the future. So ultimately data-redundancy is up to
* users to maintain.
*/
const waitForIndexStatusYellow = (
client: ElasticsearchClient,
index: string
): TaskEither.TaskEither<RetryableEsClientError, {}> => () => {
return client.cluster
.health({ index, wait_for_status: 'green', timeout: '30s' })
.health({ index, wait_for_status: 'yellow', timeout: '30s' })
.then(() => {
return Either.right({});
})
@ -259,7 +270,7 @@ export const cloneIndex = (
} else {
// Otherwise, wait until the target index has a 'green' status.
return pipe(
waitForIndexStatusGreen(client, target),
waitForIndexStatusYellow(client, target),
TaskEither.map((value) => {
/** When the index status is 'green' we know that all shards were started */
return { acknowledged: true, shardsAcknowledged: true };
@ -687,7 +698,7 @@ export const createIndex = (
} else {
// Otherwise, wait until the target index has a 'green' status.
return pipe(
waitForIndexStatusGreen(client, indexName),
waitForIndexStatusYellow(client, indexName),
TaskEither.map(() => {
/** When the index status is 'green' we know that all shards were started */
return 'create_index_succeeded';

View file

@ -213,12 +213,8 @@ describe('migration actions', () => {
}
});
it('resolves right if cloning into a new target index', async () => {
const task = cloneIndex(client, 'existing_index_with_write_block', 'clone_target_1');
expect.assertions(1);
const task = cloneIndex(
client,
'existing_index_with_write_block',
'clone_yellow_then_green_index_1'
);
await expect(task()).resolves.toMatchInlineSnapshot(`
Object {
"_tag": "Right",
@ -229,42 +225,48 @@ describe('migration actions', () => {
}
`);
});
it('resolves right after waiting for index status to be green if clone target already existed', async () => {
it('resolves right after waiting for index status to be yellow if clone target already existed', async () => {
expect.assertions(2);
// Create a yellow index
await client.indices.create({
index: 'clone_yellow_then_green_index_2',
body: {
mappings: { properties: {} },
settings: {
// Allocate 1 replica so that this index stays yellow
number_of_replicas: '1',
await client.indices
.create({
index: 'clone_red_then_yellow_index',
timeout: '5s',
body: {
mappings: { properties: {} },
settings: {
// Allocate 1 replica so that this index stays yellow
number_of_replicas: '1',
// Disable all shard allocation so that the index status is red
'index.routing.allocation.enable': 'none',
},
},
},
});
})
.catch((e) => {});
// Call clone even though the index already exists
const cloneIndexPromise = cloneIndex(
client,
'existing_index_with_write_block',
'clone_yellow_then_green_index_2'
'clone_red_then_yellow_index'
)();
let indexGreen = false;
let indexYellow = false;
setTimeout(() => {
client.indices.putSettings({
index: 'clone_red_then_yellow_index',
body: {
index: {
number_of_replicas: 0,
},
// Enable all shard allocation so that the index status goes yellow
'index.routing.allocation.enable': 'all',
},
});
indexGreen = true;
indexYellow = true;
}, 10);
await cloneIndexPromise.then((res) => {
// Assert that the promise didn't resolve before the index became green
expect(indexGreen).toBe(true);
expect(indexYellow).toBe(true);
expect(res).toMatchInlineSnapshot(`
Object {
"_tag": "Right",
@ -278,7 +280,7 @@ describe('migration actions', () => {
});
it('resolves left index_not_found_exception if the source index does not exist', async () => {
expect.assertions(1);
const task = cloneIndex(client, 'no_such_index', 'clone_yellow_then_green_index_3');
const task = cloneIndex(client, 'no_such_index', 'clone_target_3');
await expect(task()).resolves.toMatchInlineSnapshot(`
Object {
"_tag": "Left",
@ -674,7 +676,6 @@ describe('migration actions', () => {
describe('waitForPickupUpdatedMappingsTask', () => {
it('rejects if there are failures', async () => {
expect.assertions(1);
const res = (await pickupUpdatedMappings(
client,
'existing_index_with_write_block'
@ -689,7 +690,6 @@ describe('migration actions', () => {
});
});
it('rejects if there is an error', async () => {
expect.assertions(1);
const res = (await pickupUpdatedMappings(
client,
'no_such_index'
@ -703,7 +703,6 @@ describe('migration actions', () => {
`);
});
it('resolves right when successful', async () => {
expect.assertions(1);
const res = (await pickupUpdatedMappings(
client,
'existing_index_with_docs'
@ -722,7 +721,6 @@ describe('migration actions', () => {
describe('updateAndPickupMappings', () => {
it('resolves right when mappings were updated and picked up', async () => {
expect.assertions(3);
// Create an index without any mappings and insert documents into it
await createIndex(client, 'existing_index_without_mappings', {
dynamic: false as any,
@ -771,7 +769,6 @@ describe('migration actions', () => {
describe('updateAliases', () => {
describe('remove', () => {
it('resolves left index_not_found_exception when the index does not exist', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove: {
@ -793,7 +790,6 @@ describe('migration actions', () => {
});
describe('with must_exist=false', () => {
it('resolves left alias_not_found_exception when alias does not exist', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove: {
@ -815,7 +811,6 @@ describe('migration actions', () => {
});
describe('with must_exist=true', () => {
it('resolves left alias_not_found_exception when alias does not exist on specified index', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove: {
@ -835,7 +830,6 @@ describe('migration actions', () => {
`);
});
it('resolves left alias_not_found_exception when alias does not exist', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove: {
@ -858,7 +852,6 @@ describe('migration actions', () => {
});
describe('remove_index', () => {
it('left index_not_found_exception if index does not exist', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove_index: {
@ -877,7 +870,6 @@ describe('migration actions', () => {
`);
});
it('left remove_index_not_a_concrete_index when remove_index targets an alias', async () => {
expect.assertions(1);
const task = updateAliases(client, [
{
remove_index: {
@ -899,44 +891,50 @@ describe('migration actions', () => {
describe('createIndex', () => {
afterAll(async () => {
await client.indices.delete({ index: 'yellow_then_green_index' });
await client.indices.delete({ index: 'red_then_yellow_index' });
});
it('resolves right after waiting for an index status to be green if the index already existed', async () => {
it('resolves right after waiting for an index status to be yellow if the index already existed', async () => {
expect.assertions(2);
// Create a yellow index
await client.indices.create(
{
index: 'yellow_then_green_index',
body: {
mappings: { properties: {} },
settings: {
// Allocate 1 replica so that this index stays yellow
number_of_replicas: '1',
// Create a red index
await client.indices
.create(
{
index: 'red_then_yellow_index',
timeout: '5s',
body: {
mappings: { properties: {} },
settings: {
// Allocate 1 replica so that this index stays yellow
number_of_replicas: '1',
// Disable all shard allocation so that the index status is red
'index.routing.allocation.enable': 'none',
},
},
},
},
{ maxRetries: 0 /** handle retry ourselves for now */ }
);
{ maxRetries: 0 /** handle retry ourselves for now */ }
)
.catch((e) => {
/** ignore */
});
// Call createIndex even though the index already exists
const createIndexPromise = createIndex(client, 'yellow_then_green_index', undefined as any)();
let indexGreen = false;
const createIndexPromise = createIndex(client, 'red_then_yellow_index', undefined as any)();
let indexYellow = false;
setTimeout(() => {
client.indices.putSettings({
index: 'yellow_then_green_index',
index: 'red_then_yellow_index',
body: {
index: {
number_of_replicas: 0,
},
// Disable all shard allocation so that the index status is red
'index.routing.allocation.enable': 'all',
},
});
indexGreen = true;
indexYellow = true;
}, 10);
await createIndexPromise.then((res) => {
// Assert that the promise didn't resolve before the index became green
expect(indexGreen).toBe(true);
expect(indexYellow).toBe(true);
expect(res).toMatchInlineSnapshot(`
Object {
"_tag": "Right",
@ -946,7 +944,6 @@ describe('migration actions', () => {
});
});
it('rejects when there is an unexpected error creating the index', async () => {
expect.assertions(1);
// Creating an index with the same name as an existing alias to induce
// failure
await expect(
@ -957,7 +954,6 @@ describe('migration actions', () => {
describe('bulkOverwriteTransformedDocuments', () => {
it('resolves right when documents do not yet exist in the index', async () => {
expect.assertions(1);
const newDocs = ([
{ _source: { title: 'doc 5' } },
{ _source: { title: 'doc 6' } },
@ -972,7 +968,6 @@ describe('migration actions', () => {
`);
});
it('resolves right even if there were some version_conflict_engine_exception', async () => {
expect.assertions(1);
const existingDocs = ((await searchForOutdatedDocuments(
client,
'existing_index_with_docs',
@ -991,7 +986,6 @@ describe('migration actions', () => {
`);
});
it('rejects if there are errors', async () => {
expect.assertions(1);
const newDocs = ([
{ _source: { title: 'doc 5' } },
{ _source: { title: 'doc 6' } },

View file

@ -182,6 +182,21 @@ describe('migrations v2 model', () => {
versionAlias: '.kibana_7.11.0',
versionIndex: '.kibana_7.11.0_001',
};
const mappingsWithUnknownType = {
properties: {
disabled_saved_object_type: {
properties: {
value: { type: 'keyword' },
},
},
},
_meta: {
migrationMappingPropertyHashes: {
disabled_saved_object_type: '7997cf5a56cc02bdc9c93361bde732b0',
},
},
};
test('INIT -> OUTDATED_DOCUMENTS_SEARCH if .kibana is already pointing to the target index', () => {
const res: ResponseType<'INIT'> = Either.right({
'.kibana_7.11.0_001': {
@ -189,38 +204,27 @@ describe('migrations v2 model', () => {
'.kibana': {},
'.kibana_7.11.0': {},
},
mappings: {
properties: {
disabled_saved_object_type: {
properties: {
value: { type: 'keyword' },
},
},
},
_meta: {
migrationMappingPropertyHashes: {
disabled_saved_object_type: '7997cf5a56cc02bdc9c93361bde732b0',
},
},
},
mappings: mappingsWithUnknownType,
settings: {},
},
});
const newState = model(initState, res);
expect(newState.controlState).toEqual('OUTDATED_DOCUMENTS_SEARCH');
// This snapshot asserts that we merge the
// migrationMappingPropertyHashes of the existing index, but we leave
// the mappings for the disabled_saved_object_type untouched. There
// might be another Kibana instance that knows about this type and
// needs these mappings in place.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"disabled_saved_object_type": "7997cf5a56cc02bdc9c93361bde732b0",
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
@ -271,7 +275,7 @@ describe('migrations v2 model', () => {
'.kibana': {},
'.kibana_7.12.0': {},
},
mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } },
mappings: mappingsWithUnknownType,
settings: {},
},
'.kibana_7.11.0_001': {
@ -288,12 +292,37 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('.kibana_7.invalid.0_001'),
targetIndex: '.kibana_7.11.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
});
test('INIT -> SET_SOURCE_WRITE_BLOCK when migrating from a v2 migrations index (>= 7.11.0)', () => {
const res: ResponseType<'INIT'> = Either.right({
'.kibana_7.11.0_001': {
aliases: { '.kibana': {}, '.kibana_7.11.0': {} },
mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } },
mappings: mappingsWithUnknownType,
settings: {},
},
'.kibana_3': {
@ -319,6 +348,31 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('.kibana_7.11.0_001'),
targetIndex: '.kibana_7.12.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
expect(newState.retryCount).toEqual(0);
expect(newState.retryDelay).toEqual(0);
});
@ -328,7 +382,7 @@ describe('migrations v2 model', () => {
aliases: {
'.kibana': {},
},
mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } },
mappings: mappingsWithUnknownType,
settings: {},
},
});
@ -339,6 +393,31 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('.kibana_3'),
targetIndex: '.kibana_7.11.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
expect(newState.retryCount).toEqual(0);
expect(newState.retryDelay).toEqual(0);
});
@ -346,7 +425,7 @@ describe('migrations v2 model', () => {
const res: ResponseType<'INIT'> = Either.right({
'.kibana': {
aliases: {},
mappings: { properties: {}, _meta: {} },
mappings: mappingsWithUnknownType,
settings: {},
},
});
@ -357,6 +436,31 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('.kibana_pre6.5.0_001'),
targetIndex: '.kibana_7.11.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
expect(newState.retryCount).toEqual(0);
expect(newState.retryDelay).toEqual(0);
});
@ -366,7 +470,7 @@ describe('migrations v2 model', () => {
aliases: {
'my-saved-objects': {},
},
mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } },
mappings: mappingsWithUnknownType,
settings: {},
},
});
@ -386,6 +490,31 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('my-saved-objects_3'),
targetIndex: 'my-saved-objects_7.11.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
expect(newState.retryCount).toEqual(0);
expect(newState.retryDelay).toEqual(0);
});
@ -395,7 +524,7 @@ describe('migrations v2 model', () => {
aliases: {
'my-saved-objects': {},
},
mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } },
mappings: mappingsWithUnknownType,
settings: {},
},
});
@ -416,6 +545,31 @@ describe('migrations v2 model', () => {
sourceIndex: Option.some('my-saved-objects_7.11.0'),
targetIndex: 'my-saved-objects_7.12.0_001',
});
// This snapshot asserts that we disable the unknown saved object
// type. Because it's mappings are disabled, we also don't copy the
// `_meta.migrationMappingPropertyHashes` for the disabled type.
expect(newState.targetIndexMappings).toMatchInlineSnapshot(`
Object {
"_meta": Object {
"migrationMappingPropertyHashes": Object {
"new_saved_object_type": "4a11183eee21e6fbad864f7a30b39ad0",
},
},
"properties": Object {
"disabled_saved_object_type": Object {
"dynamic": false,
"properties": Object {},
},
"new_saved_object_type": Object {
"properties": Object {
"value": Object {
"type": "text",
},
},
},
},
}
`);
expect(newState.retryCount).toEqual(0);
expect(newState.retryDelay).toEqual(0);
});

View file

@ -60,13 +60,13 @@ function throwBadResponse(state: State, res: any): never {
* Merge the _meta.migrationMappingPropertyHashes mappings of an index with
* the given target mappings.
*
* @remarks Mapping updates are commutative (deeply merged) by Elasticsearch,
* except for the _meta key. The source index we're migrating from might
* contain documents created by a plugin that is disabled in the Kibana
* instance performing this migration. We merge the
* _meta.migrationMappingPropertyHashes mappings from the source index into
* the targetMappings to ensure that any `migrationPropertyHashes` for
* disabled plugins aren't lost.
* @remarks When another instance already completed a migration, the existing
* target index might contain documents and mappings created by a plugin that
* is disabled in the current Kibana instance performing this migration.
* Mapping updates are commutative (deeply merged) by Elasticsearch, except
* for the `_meta` key. By merging the `_meta.migrationMappingPropertyHashes`
* mappings from the existing target index index into the targetMappings we
* ensure that any `migrationPropertyHashes` for disabled plugins aren't lost.
*
* Right now we don't use these `migrationPropertyHashes` but it could be used
* in the future to detect if mappings were changed. If mappings weren't
@ -209,7 +209,7 @@ export const model = (currentState: State, resW: ResponseType<AllActionStates>):
// index
sourceIndex: Option.none,
targetIndex: `${stateP.indexPrefix}_${stateP.kibanaVersion}_001`,
targetIndexMappings: disableUnknownTypeMappingFields(
targetIndexMappings: mergeMigrationMappingPropertyHashes(
stateP.targetIndexMappings,
indices[aliases[stateP.currentAlias]].mappings
),
@ -242,7 +242,7 @@ export const model = (currentState: State, resW: ResponseType<AllActionStates>):
controlState: 'SET_SOURCE_WRITE_BLOCK',
sourceIndex: Option.some(source) as Option.Some<string>,
targetIndex: target,
targetIndexMappings: mergeMigrationMappingPropertyHashes(
targetIndexMappings: disableUnknownTypeMappingFields(
stateP.targetIndexMappings,
indices[source].mappings
),
@ -279,7 +279,7 @@ export const model = (currentState: State, resW: ResponseType<AllActionStates>):
controlState: 'LEGACY_SET_WRITE_BLOCK',
sourceIndex: Option.some(legacyReindexTarget) as Option.Some<string>,
targetIndex: target,
targetIndexMappings: mergeMigrationMappingPropertyHashes(
targetIndexMappings: disableUnknownTypeMappingFields(
stateP.targetIndexMappings,
indices[stateP.legacyIndex].mappings
),

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -44,7 +45,7 @@ export const registerBulkCreateRoute = (router: IRouter, { coreUsageData }: Rout
),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { overwrite } = req.query;
const usageStatsClient = coreUsageData.getClient();

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -28,7 +29,7 @@ export const registerBulkGetRoute = (router: IRouter, { coreUsageData }: RouteDe
),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {});

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -39,7 +40,7 @@ export const registerBulkUpdateRoute = (router: IRouter, { coreUsageData }: Rout
),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {});

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -43,7 +44,7 @@ export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDep
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { type, id } = req.params;
const { overwrite } = req.query;
const {

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -28,7 +29,7 @@ export const registerDeleteRoute = (router: IRouter, { coreUsageData }: RouteDep
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { type, id } = req.params;
const { force } = req.query;

View file

@ -18,7 +18,7 @@ import {
SavedObjectsExportByObjectOptions,
SavedObjectsExportError,
} from '../export';
import { validateTypes, validateObjects } from './utils';
import { validateTypes, validateObjects, catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
config: SavedObjectConfig;
@ -163,7 +163,7 @@ export const registerExportRoute = (
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const cleaned = cleanOptions(req.body);
const supportedTypes = context.core.savedObjects.typeRegistry
.getImportableAndExportableTypes()

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -49,7 +50,7 @@ export const registerFindRoute = (router: IRouter, { coreUsageData }: RouteDepen
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const query = req.query;
const namespaces =

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -25,7 +26,7 @@ export const registerGetRoute = (router: IRouter, { coreUsageData }: RouteDepend
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { type, id } = req.params;
const usageStatsClient = coreUsageData.getClient();

View file

@ -13,7 +13,7 @@ import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { SavedObjectConfig } from '../saved_objects_config';
import { SavedObjectsImportError } from '../import';
import { createSavedObjectsStreamFromNdJson } from './utils';
import { catchAndReturnBoomErrors, createSavedObjectsStreamFromNdJson } from './utils';
interface RouteDependencies {
config: SavedObjectConfig;
@ -61,7 +61,7 @@ export const registerImportRoute = (
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { overwrite, createNewCopies } = req.query;
const usageStatsClient = coreUsageData.getClient();

View file

@ -8,6 +8,7 @@
import { IRouter } from '../../http';
import { IKibanaMigrator } from '../migrations';
import { catchAndReturnBoomErrors } from './utils';
export const registerMigrateRoute = (
router: IRouter,
@ -21,7 +22,7 @@ export const registerMigrateRoute = (
tags: ['access:migrateSavedObjects'],
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const migrator = await migratorPromise;
await migrator.runMigrations({ rerun: true });
return res.ok({

View file

@ -13,8 +13,7 @@ import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { SavedObjectConfig } from '../saved_objects_config';
import { SavedObjectsImportError } from '../import';
import { createSavedObjectsStreamFromNdJson } from './utils';
import { catchAndReturnBoomErrors, createSavedObjectsStreamFromNdJson } from './utils';
interface RouteDependencies {
config: SavedObjectConfig;
coreUsageData: CoreUsageDataSetup;
@ -69,7 +68,7 @@ export const registerResolveImportErrorsRoute = (
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { createNewCopies } = req.query;
const usageStatsClient = coreUsageData.getClient();

View file

@ -9,6 +9,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '../../http';
import { CoreUsageDataSetup } from '../../core_usage_data';
import { catchAndReturnBoomErrors } from './utils';
interface RouteDependencies {
coreUsageData: CoreUsageDataSetup;
@ -38,7 +39,7 @@ export const registerUpdateRoute = (router: IRouter, { coreUsageData }: RouteDep
}),
},
},
router.handleLegacyErrors(async (context, req, res) => {
catchAndReturnBoomErrors(async (context, req, res) => {
const { type, id } = req.params;
const { attributes, version, references } = req.body;
const options = { version, references };

View file

@ -9,6 +9,15 @@
import { createSavedObjectsStreamFromNdJson, validateTypes, validateObjects } from './utils';
import { Readable } from 'stream';
import { createPromiseFromStreams, createConcatStream } from '@kbn/utils';
import { catchAndReturnBoomErrors } from './utils';
import Boom from '@hapi/boom';
import {
KibanaRequest,
RequestHandler,
RequestHandlerContext,
KibanaResponseFactory,
kibanaResponseFactory,
} from '../../';
async function readStreamToCompletion(stream: Readable) {
return createPromiseFromStreams([stream, createConcatStream([])]);
@ -143,3 +152,69 @@ describe('validateObjects', () => {
).toBeUndefined();
});
});
describe('catchAndReturnBoomErrors', () => {
let context: RequestHandlerContext;
let request: KibanaRequest<any, any, any>;
let response: KibanaResponseFactory;
const createHandler = (handler: () => any): RequestHandler<any, any, any> => () => {
return handler();
};
beforeEach(() => {
context = {} as any;
request = {} as any;
response = kibanaResponseFactory;
});
it('should pass-though call parameters to the handler', async () => {
const handler = jest.fn();
const wrapped = catchAndReturnBoomErrors(handler);
await wrapped(context, request, response);
expect(handler).toHaveBeenCalledWith(context, request, response);
});
it('should pass-though result from the handler', async () => {
const handler = createHandler(() => {
return 'handler-response';
});
const wrapped = catchAndReturnBoomErrors(handler);
const result = await wrapped(context, request, response);
expect(result).toBe('handler-response');
});
it('should intercept and convert thrown Boom errors', async () => {
const handler = createHandler(() => {
throw Boom.notFound('not there');
});
const wrapped = catchAndReturnBoomErrors(handler);
const result = await wrapped(context, request, response);
expect(result.status).toBe(404);
expect(result.payload).toEqual({
error: 'Not Found',
message: 'not there',
statusCode: 404,
});
});
it('should re-throw non-Boom errors', async () => {
const handler = createHandler(() => {
throw new Error('something went bad');
});
const wrapped = catchAndReturnBoomErrors(handler);
await expect(wrapped(context, request, response)).rejects.toMatchInlineSnapshot(
`[Error: something went bad]`
);
});
it('should re-throw Boom internal/500 errors', async () => {
const handler = createHandler(() => {
throw Boom.internal();
});
const wrapped = catchAndReturnBoomErrors(handler);
await expect(wrapped(context, request, response)).rejects.toMatchInlineSnapshot(
`[Error: Internal Server Error]`
);
});
});

View file

@ -7,7 +7,11 @@
*/
import { Readable } from 'stream';
import { SavedObject, SavedObjectsExportResultDetails } from 'src/core/server';
import {
RequestHandlerWrapper,
SavedObject,
SavedObjectsExportResultDetails,
} from 'src/core/server';
import {
createSplitStream,
createMapStream,
@ -16,6 +20,7 @@ import {
createListStream,
createConcatStream,
} from '@kbn/utils';
import Boom from '@hapi/boom';
export async function createSavedObjectsStreamFromNdJson(ndJsonStream: Readable) {
const savedObjects = await createPromiseFromStreams([
@ -52,3 +57,30 @@ export function validateObjects(
.join(', ')}`;
}
}
/**
* Catches errors thrown by saved object route handlers and returns an error
* with the payload and statusCode of the boom error.
*
* This is very close to the core `router.handleLegacyErrors` except that it
* throws internal errors (statusCode: 500) so that the internal error's
* message get logged by Core.
*
* TODO: Remove once https://github.com/elastic/kibana/issues/65291 is fixed.
*/
export const catchAndReturnBoomErrors: RequestHandlerWrapper = (handler) => {
return async (context, request, response) => {
try {
return await handler(context, request, response);
} catch (e) {
if (Boom.isBoom(e) && e.output.statusCode !== 500) {
return response.customError({
body: e.output.payload,
statusCode: e.output.statusCode,
headers: e.output.headers as { [key: string]: string },
});
}
throw e;
}
};
};

View file

@ -109,6 +109,27 @@ describe('savedObjectsClient/decorateEsError', () => {
expect(SavedObjectsErrorHelpers.isNotFoundError(genericError)).toBe(true);
});
it('if saved objects index does not exist makes NotFound a SavedObjectsClient/generalError', () => {
const error = new esErrors.ResponseError(
elasticsearchClientMock.createApiResponse({
statusCode: 404,
body: {
error: {
reason:
'no such index [.kibana_8.0.0] and [require_alias] request flag is [true] and [.kibana_8.0.0] is not an alias',
},
},
})
);
expect(SavedObjectsErrorHelpers.isGeneralError(error)).toBe(false);
const genericError = decorateEsError(error);
expect(genericError.message).toEqual(
`Saved object index alias [.kibana_8.0.0] not found: Response Error`
);
expect(genericError.output.statusCode).toBe(500);
expect(SavedObjectsErrorHelpers.isGeneralError(error)).toBe(true);
});
it('makes BadRequest a SavedObjectsClient/BadRequest error', () => {
const error = new esErrors.ResponseError(
elasticsearchClientMock.createApiResponse({ statusCode: 400 })

View file

@ -63,6 +63,12 @@ export function decorateEsError(error: EsErrors) {
}
if (responseErrors.isNotFound(error.statusCode)) {
const match = error?.meta?.body?.error?.reason?.match(
/no such index \[(.+)\] and \[require_alias\] request flag is \[true\] and \[.+\] is not an alias/
);
if (match?.length > 0) {
return SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError(error, match[1]);
}
return SavedObjectsErrorHelpers.createGenericNotFoundError();
}

View file

@ -135,6 +135,19 @@ export class SavedObjectsErrorHelpers {
return decorate(Boom.notFound(), CODE_NOT_FOUND, 404);
}
public static createIndexAliasNotFoundError(alias: string) {
return SavedObjectsErrorHelpers.decorateIndexAliasNotFoundError(Boom.internal(), alias);
}
public static decorateIndexAliasNotFoundError(error: Error, alias: string) {
return decorate(
error,
CODE_GENERAL_ERROR,
500,
`Saved object index alias [${alias}] not found`
);
}
public static isNotFoundError(error: Error | DecoratedError) {
return isSavedObjectsClientError(error) && error[code] === CODE_NOT_FOUND;
}
@ -185,4 +198,8 @@ export class SavedObjectsErrorHelpers {
public static decorateGeneralError(error: Error, reason?: string) {
return decorate(error, CODE_GENERAL_ERROR, 500, reason);
}
public static isGeneralError(error: Error | DecoratedError) {
return isSavedObjectsClientError(error) && error[code] === CODE_GENERAL_ERROR;
}
}

View file

@ -18,6 +18,7 @@ import { DocumentMigrator } from '../../migrations/core/document_migrator';
import { mockKibanaMigrator } from '../../migrations/kibana/kibana_migrator.mock';
import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks';
import { esKuery } from '../../es_query';
import { errors as EsErrors } from '@elastic/elasticsearch';
const { nodeTypes } = esKuery;
jest.mock('./search_dsl/search_dsl', () => ({ getSearchDsl: jest.fn() }));
@ -4341,8 +4342,14 @@ describe('SavedObjectsRepository', () => {
});
it(`throws when ES is unable to find the document during update`, async () => {
const notFoundError = new EsErrors.ResponseError(
elasticsearchClientMock.createApiResponse({
statusCode: 404,
body: { error: { type: 'es_type', reason: 'es_reason' } },
})
);
client.update.mockResolvedValueOnce(
elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 })
elasticsearchClientMock.createErrorTransportRequestPromise(notFoundError)
);
await expectNotFoundError(type, id);
expect(client.update).toHaveBeenCalledTimes(1);

View file

@ -299,6 +299,7 @@ export class SavedObjectsRepository {
refresh,
body: raw._source,
...(overwrite && version ? decodeRequestVersion(version) : {}),
require_alias: true,
};
const { body } =
@ -469,6 +470,7 @@ export class SavedObjectsRepository {
const bulkResponse = bulkCreateParams.length
? await this.client.bulk({
refresh,
require_alias: true,
body: bulkCreateParams,
})
: undefined;
@ -1117,8 +1119,8 @@ export class SavedObjectsRepository {
...(Array.isArray(references) && { references }),
};
const { body, statusCode } = await this.client.update(
{
const { body } = await this.client
.update({
id: this._serializer.generateRawId(namespace, type, id),
index: this.getIndexForType(type),
...getExpectedVersionProperties(version, preflightResult),
@ -1128,14 +1130,15 @@ export class SavedObjectsRepository {
doc,
},
_source_includes: ['namespace', 'namespaces', 'originId'],
},
{ ignore: [404] }
);
if (statusCode === 404) {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
require_alias: true,
})
.catch((err) => {
if (SavedObjectsErrorHelpers.isNotFoundError(err)) {
// see "404s from missing index" above
throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id);
}
throw err;
});
const { originId } = body.get._source;
let namespaces = [];
@ -1496,6 +1499,7 @@ export class SavedObjectsRepository {
refresh,
body: bulkUpdateParams,
_source_includes: ['originId'],
require_alias: true,
})
: undefined;
@ -1712,6 +1716,7 @@ export class SavedObjectsRepository {
id: raw._id,
index: this.getIndexForType(type),
refresh,
require_alias: true,
_source: 'true',
body: {
script: {
@ -1933,12 +1938,18 @@ export class SavedObjectsRepository {
}
}
function getBulkOperationError(error: { type: string; reason?: string }, type: string, id: string) {
function getBulkOperationError(
error: { type: string; reason?: string; index?: string },
type: string,
id: string
) {
switch (error.type) {
case 'version_conflict_engine_exception':
return errorContent(SavedObjectsErrorHelpers.createConflictError(type, id));
case 'document_missing_exception':
return errorContent(SavedObjectsErrorHelpers.createGenericNotFoundError(type, id));
case 'index_not_found_exception':
return errorContent(SavedObjectsErrorHelpers.createIndexAliasNotFoundError(error.index!));
default:
return {
message: error.reason || JSON.stringify(error),

View file

@ -2335,6 +2335,8 @@ export class SavedObjectsErrorHelpers {
// (undocumented)
static createGenericNotFoundError(type?: string | null, id?: string | null): DecoratedError;
// (undocumented)
static createIndexAliasNotFoundError(alias: string): DecoratedError;
// (undocumented)
static createInvalidVersionError(versionInput?: string): DecoratedError;
// (undocumented)
static createTooManyRequestsError(type: string, id: string): DecoratedError;
@ -2353,6 +2355,8 @@ export class SavedObjectsErrorHelpers {
// (undocumented)
static decorateGeneralError(error: Error, reason?: string): DecoratedError;
// (undocumented)
static decorateIndexAliasNotFoundError(error: Error, alias: string): DecoratedError;
// (undocumented)
static decorateNotAuthorizedError(error: Error, reason?: string): DecoratedError;
// (undocumented)
static decorateRequestEntityTooLargeError(error: Error, reason?: string): DecoratedError;
@ -2369,6 +2373,8 @@ export class SavedObjectsErrorHelpers {
// (undocumented)
static isForbiddenError(error: Error | DecoratedError): boolean;
// (undocumented)
static isGeneralError(error: Error | DecoratedError): boolean;
// (undocumented)
static isInvalidVersionError(error: Error | DecoratedError): boolean;
// (undocumented)
static isNotAuthorizedError(error: Error | DecoratedError): boolean;

View file

@ -8,7 +8,7 @@
import { getServices, chance } from './lib';
export function docExistsSuite() {
export const docExistsSuite = (savedObjectsIndex: string) => () => {
async function setup(options: any = {}) {
const { initialSettings } = options;
@ -16,7 +16,7 @@ export function docExistsSuite() {
// delete the kibana index to ensure we start fresh
await callCluster('deleteByQuery', {
index: kbnServer.config.get('kibana.index'),
index: savedObjectsIndex,
body: {
conflicts: 'proceed',
query: { match_all: {} },
@ -212,4 +212,4 @@ export function docExistsSuite() {
});
});
});
}
};

View file

@ -8,7 +8,7 @@
import { getServices, chance } from './lib';
export function docMissingSuite() {
export const docMissingSuite = (savedObjectsIndex: string) => () => {
// ensure the kibana index has no documents
beforeEach(async () => {
const { kbnServer, callCluster } = getServices();
@ -22,7 +22,7 @@ export function docMissingSuite() {
// delete all docs from kibana index to ensure savedConfig is not found
await callCluster('deleteByQuery', {
index: kbnServer.config.get('kibana.index'),
index: savedObjectsIndex,
body: {
query: { match_all: {} },
},
@ -136,4 +136,4 @@ export function docMissingSuite() {
});
});
});
}
};

View file

@ -8,7 +8,7 @@
import { getServices, chance } from './lib';
export function docMissingAndIndexReadOnlySuite() {
export const docMissingAndIndexReadOnlySuite = (savedObjectsIndex: string) => () => {
// ensure the kibana index has no documents
beforeEach(async () => {
const { kbnServer, callCluster } = getServices();
@ -22,7 +22,7 @@ export function docMissingAndIndexReadOnlySuite() {
// delete all docs from kibana index to ensure savedConfig is not found
await callCluster('deleteByQuery', {
index: kbnServer.config.get('kibana.index'),
index: savedObjectsIndex,
body: {
query: { match_all: {} },
},
@ -30,7 +30,7 @@ export function docMissingAndIndexReadOnlySuite() {
// set the index to read only
await callCluster('indices.putSettings', {
index: kbnServer.config.get('kibana.index'),
index: savedObjectsIndex,
body: {
index: {
blocks: {
@ -42,11 +42,11 @@ export function docMissingAndIndexReadOnlySuite() {
});
afterEach(async () => {
const { kbnServer, callCluster } = getServices();
const { callCluster } = getServices();
// disable the read only block
await callCluster('indices.putSettings', {
index: kbnServer.config.get('kibana.index'),
index: savedObjectsIndex,
body: {
index: {
blocks: {
@ -142,4 +142,4 @@ export function docMissingAndIndexReadOnlySuite() {
});
});
});
}
};

View file

@ -6,20 +6,25 @@
* Side Public License, v 1.
*/
import { Env } from '@kbn/config';
import { REPO_ROOT } from '@kbn/dev-utils';
import { getEnvOptions } from '@kbn/config/target/mocks';
import { startServers, stopServers } from './lib';
import { docExistsSuite } from './doc_exists';
import { docMissingSuite } from './doc_missing';
import { docMissingAndIndexReadOnlySuite } from './doc_missing_and_index_read_only';
const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
const savedObjectIndex = `.kibana_${kibanaVersion}_001`;
describe('uiSettings/routes', function () {
jest.setTimeout(10000);
beforeAll(startServers);
/* eslint-disable jest/valid-describe */
describe('doc missing', docMissingSuite);
describe('doc missing and index readonly', docMissingAndIndexReadOnlySuite);
describe('doc exists', docExistsSuite);
describe('doc missing', docMissingSuite(savedObjectIndex));
describe('doc missing and index readonly', docMissingAndIndexReadOnlySuite(savedObjectIndex));
describe('doc exists', docExistsSuite(savedObjectIndex));
/* eslint-enable jest/valid-describe */
afterAll(stopServers);
});

View file

@ -37,9 +37,6 @@ export async function startServers() {
adjustTimeout: (t) => jest.setTimeout(t),
settings: {
kbn: {
migrations: {
enableV2: false,
},
uiSettings: {
overrides: {
foo: 'bar',

View file

@ -40,7 +40,7 @@ const DEFAULTS_SETTINGS = {
},
logging: { silent: true },
plugins: {},
migrations: { skip: true },
migrations: { skip: false },
};
const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = {

View file

@ -16,7 +16,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
before(async () => {
await esArchiver.load('empty_kibana');
await esArchiver.emptyKibanaIndex();
await PageObjects.common.navigateToApp('kibanaOverview');
});
@ -25,7 +25,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
useActualUrl: true,
});
await PageObjects.home.removeSampleDataSet('flights');
await esArchiver.unload('empty_kibana');
});
it('Getting started view', async () => {

View file

@ -11,11 +11,15 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
const MILLISECOND_IN_WEEK = 1000 * 60 * 60 * 24 * 7;
describe('sample data apis', () => {
before(async () => {
await esArchiver.emptyKibanaIndex();
});
describe('list', () => {
it('should return list of sample data sets with installed status', async () => {
const resp = await supertest.get(`/api/sample_data`).set('kbn-xsrf', 'kibana').expect(200);

View file

@ -97,10 +97,11 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 200 with individual responses', async () =>
it('should return 200 with errors', async () => {
await new Promise((resolve) => setTimeout(resolve, 2000));
await supertest
.post('/api/saved_objects/_bulk_create')
.send(BULK_REQUESTS)
@ -109,38 +110,27 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body).to.eql({
saved_objects: [
{
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
updated_at: resp.body.saved_objects[0].updated_at,
version: resp.body.saved_objects[0].version,
attributes: {
title: 'An existing visualization',
id: BULK_REQUESTS[0].id,
type: BULK_REQUESTS[0].type,
error: {
error: 'Internal Server Error',
message: 'An internal server error occurred',
statusCode: 500,
},
references: [],
namespaces: ['default'],
migrationVersion: {
visualization: resp.body.saved_objects[0].migrationVersion.visualization,
},
coreMigrationVersion: KIBANA_VERSION, // updated from 1.2.3 to the latest kibana version
},
{
type: 'dashboard',
id: 'a01b2f57-fcfd-4864-b735-09e28f0d815e',
updated_at: resp.body.saved_objects[1].updated_at,
version: resp.body.saved_objects[1].version,
attributes: {
title: 'A great new dashboard',
id: BULK_REQUESTS[1].id,
type: BULK_REQUESTS[1].type,
error: {
error: 'Internal Server Error',
message: 'An internal server error occurred',
statusCode: 500,
},
references: [],
namespaces: ['default'],
migrationVersion: {
dashboard: resp.body.saved_objects[1].migrationVersion.dashboard,
},
coreMigrationVersion: KIBANA_VERSION,
},
],
});
}));
});
});
});
});
}

View file

@ -108,7 +108,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 200 with individual responses', async () =>

View file

@ -235,10 +235,10 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return generic 404', async () => {
it('should return 200 with errors', async () => {
const response = await supertest
.put(`/api/saved_objects/_bulk_update`)
.send([
@ -267,9 +267,9 @@ export default function ({ getService }: FtrProviderContext) {
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
type: 'visualization',
error: {
statusCode: 404,
error: 'Not Found',
message: 'Saved object [visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab] not found',
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred',
},
});
@ -277,9 +277,9 @@ export default function ({ getService }: FtrProviderContext) {
id: 'be3733a0-9efe-11e7-acb3-3dab96693fab',
type: 'dashboard',
error: {
statusCode: 404,
error: 'Not Found',
message: 'Saved object [dashboard/be3733a0-9efe-11e7-acb3-3dab96693fab] not found',
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred',
},
});
});

View file

@ -83,10 +83,10 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 200 and create kibana index', async () => {
it('should return 500 and not auto-create saved objects index', async () => {
await supertest
.post(`/api/saved_objects/visualization`)
.send({
@ -94,50 +94,16 @@ export default function ({ getService }: FtrProviderContext) {
title: 'My favorite vis',
},
})
.expect(200)
.expect(500)
.then((resp) => {
// loose uuid validation
expect(resp.body)
.to.have.property('id')
.match(/^[0-9a-f-]{36}$/);
// loose ISO8601 UTC time with milliseconds validation
expect(resp.body)
.to.have.property('updated_at')
.match(/^[\d-]{10}T[\d:\.]{12}Z$/);
expect(resp.body).to.eql({
id: resp.body.id,
type: 'visualization',
migrationVersion: resp.body.migrationVersion,
coreMigrationVersion: KIBANA_VERSION,
updated_at: resp.body.updated_at,
version: resp.body.version,
attributes: {
title: 'My favorite vis',
},
references: [],
namespaces: ['default'],
error: 'Internal Server Error',
message: 'An internal server error occurred.',
statusCode: 500,
});
expect(resp.body.migrationVersion).to.be.ok();
});
expect((await es.indices.exists({ index: '.kibana' })).body).to.be(true);
});
it('result should have the latest coreMigrationVersion', async () => {
await supertest
.post(`/api/saved_objects/visualization`)
.send({
attributes: {
title: 'My favorite vis',
},
coreMigrationVersion: '1.2.3',
})
.expect(200)
.then((resp) => {
expect(resp.body.coreMigrationVersion).to.eql(KIBANA_VERSION);
});
expect((await es.indices.exists({ index: '.kibana' })).body).to.be(false);
});
});
});

View file

@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('returns generic 404 when kibana index is missing', async () =>

View file

@ -534,7 +534,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return empty response', async () => {

View file

@ -40,7 +40,7 @@ export default function ({ getService }: FtrProviderContext) {
{
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
version: 'WzIsMV0=',
version: 'WzE4LDJd',
attributes: {
title: 'Count of requests',
},
@ -137,7 +137,7 @@ export default function ({ getService }: FtrProviderContext) {
{
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
version: 'WzIsMV0=',
version: 'WzE4LDJd',
attributes: {
title: 'Count of requests',
},
@ -174,7 +174,7 @@ export default function ({ getService }: FtrProviderContext) {
{
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
version: 'WzIsMV0=',
version: 'WzE4LDJd',
attributes: {
title: 'Count of requests',
},
@ -209,7 +209,7 @@ export default function ({ getService }: FtrProviderContext) {
score: 0,
type: 'visualization',
updated_at: '2017-09-21T18:51:23.794Z',
version: 'WzYsMV0=',
version: 'WzIyLDJd',
},
],
});
@ -256,7 +256,7 @@ export default function ({ getService }: FtrProviderContext) {
migrationVersion: resp.body.saved_objects[0].migrationVersion,
coreMigrationVersion: KIBANA_VERSION,
updated_at: '2017-09-21T18:51:23.794Z',
version: 'WzIsMV0=',
version: 'WzE4LDJd',
},
],
});
@ -426,11 +426,11 @@ export default function ({ getService }: FtrProviderContext) {
}));
});
describe.skip('without kibana index', () => {
describe('without kibana index', () => {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 200 with empty response', async () =>

View file

@ -78,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return basic 404 without mentioning index', async () =>

View file

@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const esDeleteAllIndices = getService('esDeleteAllIndices');
describe('resolve_import_errors', () => {
// mock success results including metadata
@ -34,7 +35,11 @@ export default function ({ getService }: FtrProviderContext) {
describe('without kibana index', () => {
// Cleanup data that got created in import
after(() => esArchiver.unload('saved_objects/basic'));
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana*')
);
it('should return 200 and import nothing when empty parameters are passed in', async () => {
await supertest
@ -51,7 +56,7 @@ export default function ({ getService }: FtrProviderContext) {
});
});
it('should return 200 and import everything when overwrite parameters contains all objects', async () => {
it('should return 200 with internal server errors', async () => {
await supertest
.post('/api/saved_objects/_resolve_import_errors')
.field(
@ -78,12 +83,42 @@ export default function ({ getService }: FtrProviderContext) {
.expect(200)
.then((resp) => {
expect(resp.body).to.eql({
success: true,
successCount: 3,
successResults: [
{ ...indexPattern, overwrite: true },
{ ...visualization, overwrite: true },
{ ...dashboard, overwrite: true },
successCount: 0,
success: false,
errors: [
{
...indexPattern,
...{ title: indexPattern.meta.title },
overwrite: true,
error: {
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred',
type: 'unknown',
},
},
{
...visualization,
...{ title: visualization.meta.title },
overwrite: true,
error: {
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred',
type: 'unknown',
},
},
{
...dashboard,
...{ title: dashboard.meta.title },
overwrite: true,
error: {
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred',
type: 'unknown',
},
},
],
warnings: [],
});

View file

@ -121,10 +121,10 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return generic 404', async () =>
it('should return 500', async () =>
await supertest
.put(`/api/saved_objects/visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab`)
.send({
@ -132,13 +132,12 @@ export default function ({ getService }: FtrProviderContext) {
title: 'My second favorite vis',
},
})
.expect(404)
.expect(500)
.then((resp) => {
expect(resp.body).eql({
statusCode: 404,
error: 'Not Found',
message:
'Saved object [visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab] not found',
statusCode: 500,
error: 'Internal Server Error',
message: 'An internal server error occurred.',
});
}));
});

View file

@ -42,7 +42,7 @@ export default function ({ getService }: FtrProviderContext) {
{
type: 'visualization',
id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab',
version: 'WzIsMV0=',
version: 'WzE4LDJd',
attributes: {
title: 'Count of requests',
},
@ -184,7 +184,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 200 with empty response', async () =>

View file

@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) {
before(
async () =>
// just in case the kibana server has recreated it
await esDeleteAllIndices('.kibana')
await esDeleteAllIndices('.kibana*')
);
it('should return 404 for object that no longer exists', async () =>

View file

@ -17,6 +17,7 @@ export default function ({ getService }: FtrProviderContext) {
describe('search', () => {
before(async () => {
await esArchiver.emptyKibanaIndex();
await esArchiver.loadIfNeeded('../../../functional/fixtures/es_archiver/logstash_functional');
});

View file

@ -14,10 +14,13 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function optInTest({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
describe('/api/telemetry/v2/optIn API', () => {
let defaultAttributes: TelemetrySavedObjectAttributes;
let kibanaVersion: any;
before(async () => {
await esArchiver.emptyKibanaIndex();
const kibanaVersionAccessor = kibanaServer.version;
kibanaVersion = await kibanaVersionAccessor.get();
defaultAttributes =

View file

@ -177,6 +177,7 @@ export default function ({ getService }: FtrProviderContext) {
describe('basic behaviour', () => {
let savedObjectIds: string[] = [];
before('create application usage entries', async () => {
await esArchiver.emptyKibanaIndex();
savedObjectIds = await Promise.all([
createSavedObject(),
createSavedObject('appView1'),

View file

@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
const createUiCounterEvent = (eventName: string, type: UiCounterMetricType, count = 1) => ({
@ -24,6 +25,9 @@ export default function ({ getService }: FtrProviderContext) {
// FLAKY: https://github.com/elastic/kibana/issues/85086
describe.skip('UI Counters API', () => {
before(async () => {
await esArchiver.emptyKibanaIndex();
});
const dayDate = moment().format('DDMMYYYY');
it('stores ui counter events in savedObjects', async () => {

View file

@ -13,6 +13,7 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
const createStatsMetric = (
@ -34,6 +35,10 @@ export default function ({ getService }: FtrProviderContext) {
});
describe('ui_metric savedObject data', () => {
before(async () => {
await esArchiver.emptyKibanaIndex();
});
it('increments the count field in the document defined by the {app}/{action_type} path', async () => {
const reportManager = new ReportManager();
const uiStatsMetric = createStatsMetric('myEvent');

View file

@ -61,8 +61,6 @@ export default function () {
...(!!process.env.CODE_COVERAGE
? [`--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'coverage')}`]
: []),
// Disable v2 migrations in tests for now
'--migrations.enableV2=false',
],
},
services,

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload'];
const ES_ARCHIVER_LOAD_METHODS = ['load', 'loadIfNeeded', 'unload', 'emptyKibanaIndex'];
const KIBANA_INDEX = '.kibana';
export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }) {
@ -25,7 +25,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 || stats[key].deleted)
(key) => key.includes(KIBANA_INDEX) && stats[key].created
);
// if the kibana index was created by the esArchiver then update the uiSettings

View file

@ -27,9 +27,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe.skip('import objects', function describeIndexTests() {
describe('.ndjson file', () => {
beforeEach(async function () {
await esArchiver.load('management');
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await esArchiver.load('management');
await PageObjects.settings.clickKibanaSavedObjects();
});
@ -213,10 +213,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('.json file', () => {
beforeEach(async function () {
// delete .kibana index and then wait for Kibana to re-create it
await esArchiver.load('saved_objects_imports');
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await esArchiver.load('saved_objects_imports');
await PageObjects.settings.clickKibanaSavedObjects();
});

View file

@ -12,10 +12,11 @@ export default function ({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
const retry = getService('retry');
const PageObjects = getPageObjects(['settings']);
const esArchiver = getService('esArchiver');
describe('index pattern filter', function describeIndexTests() {
before(async function () {
// delete .kibana index and then wait for Kibana to re-create it
await esArchiver.emptyKibanaIndex();
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();

View file

@ -19,7 +19,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
describe('index pattern empty view', () => {
before(async () => {
await esArchiver.load('empty_kibana');
await esArchiver.emptyKibanaIndex();
await esArchiver.unload('logstash_functional');
await esArchiver.unload('makelogs');
await kibanaServer.uiSettings.replace({});
@ -27,7 +27,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
after(async () => {
await esArchiver.unload('empty_kibana');
await esArchiver.loadIfNeeded('makelogs');
// @ts-expect-error
await es.transport.request({

View file

@ -18,14 +18,13 @@ export default function ({ getService, getPageObjects }) {
describe('mgmt saved objects', function describeIndexTests() {
beforeEach(async function () {
await esArchiver.load('empty_kibana');
await esArchiver.emptyKibanaIndex();
await esArchiver.load('discover');
await PageObjects.settings.navigateTo();
});
afterEach(async function () {
await esArchiver.unload('discover');
await esArchiver.load('empty_kibana');
});
it('should import saved objects mgmt', async function () {

View file

@ -20,6 +20,7 @@ export default function ({ getService, getPageObjects }) {
const EXPECTED_FIELD_COUNT = '10006';
before(async function () {
await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader']);
await esArchiver.emptyKibanaIndex();
await esArchiver.loadIfNeeded('large_fields');
await PageObjects.settings.createIndexPattern('testhuge', 'date');
});

View file

@ -14,13 +14,11 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
describe('management', function () {
before(async () => {
await esArchiver.unload('logstash_functional');
await esArchiver.load('empty_kibana');
await esArchiver.loadIfNeeded('makelogs');
});
after(async () => {
await esArchiver.unload('makelogs');
await esArchiver.unload('empty_kibana');
});
describe('', function () {

View file

@ -12,7 +12,6 @@ import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const find = getService('find');
const security = getService('security');
const { visualize, visEditor } = getPageObjects(['visualize', 'visEditor']);
@ -53,7 +52,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await esArchiver.loadIfNeeded('logstash_functional');
await esArchiver.loadIfNeeded('long_window_logstash');
await esArchiver.load('visualize');
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
await security.testUser.restoreDefaults();
});
});

View file

@ -19,6 +19,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
const find = getService('find');
const retry = getService('retry');
const deployment = getService('deployment');
const esArchiver = getService('esArchiver');
const loadingScreenNotShown = async () =>
expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false);
@ -50,6 +51,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
describe('ui applications', function describeIndexTests() {
before(async () => {
await esArchiver.emptyKibanaIndex();
await PageObjects.common.navigateToApp('foo');
});

View file

@ -12,8 +12,12 @@ import '../../plugins/core_provider_plugin/types';
export default function ({ getService }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('index patterns', function () {
before(async () => {
await esArchiver.emptyKibanaIndex();
});
let indexPatternId = '';
it('can create an index pattern', async () => {

View file

@ -10,10 +10,15 @@ import path from 'path';
import expect from '@kbn/expect';
import { PluginFunctionalProviderContext } from '../../services';
export default function ({ getPageObjects }: PluginFunctionalProviderContext) {
export default function ({ getPageObjects, getService }: PluginFunctionalProviderContext) {
const PageObjects = getPageObjects(['common', 'settings', 'header', 'savedObjects']);
const esArchiver = getService('esArchiver');
describe('import warnings', () => {
before(async () => {
await esArchiver.emptyKibanaIndex();
});
beforeEach(async () => {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();

View file

@ -31,6 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
before(async () => {
await browser.setLocalStorageItem('insecureClusterWarningVisibility', '');
await esArchiver.unload('hamlet');
await esArchiver.emptyKibanaIndex();
});
it('should not warn when the cluster contains no user data', async () => {

View file

@ -13,6 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const retry = getService('retry');
const browser = getService('browser');
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const log = getService('log');
const pieChart = getService('pieChart');
const find = getService('find');
@ -30,6 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
describe('sample data dashboard', function describeIndexTests() {
before(async () => {
await esArchiver.emptyKibanaIndex();
await PageObjects.common.sleep(5000);
await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', {
useActualUrl: true,

View file

@ -70,7 +70,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('shows all saved objects', async () => {
const objects = await PageObjects.savedObjects.getRowTitles();
expect(objects).to.eql([
'Advanced Settings [6.0.0]',
`Advanced Settings [${version}]`,
'A Dashboard',
'logstash-*',
@ -81,10 +80,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('can view all saved objects in applications', async () => {
const bools = await PageObjects.savedObjects.getTableSummary();
expect(bools).to.eql([
{
title: 'Advanced Settings [6.0.0]',
canViewInApp: false,
},
{
title: `Advanced Settings [${version}]`,
canViewInApp: false,
@ -189,7 +184,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('shows all saved objects', async () => {
const objects = await PageObjects.savedObjects.getRowTitles();
expect(objects).to.eql([
'Advanced Settings [6.0.0]',
`Advanced Settings [${version}]`,
'A Dashboard',
'logstash-*',
@ -200,10 +194,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('cannot view any saved objects in applications', async () => {
const bools = await PageObjects.savedObjects.getTableSummary();
expect(bools).to.eql([
{
title: 'Advanced Settings [6.0.0]',
canViewInApp: false,
},
{
title: `Advanced Settings [${version}]`,
canViewInApp: false,

View file

@ -66,20 +66,3 @@
}
}
}
{
"type": "doc",
"value": {
"index": ".kibana",
"type": "doc",
"id": "config:6.0.0",
"source": {
"config": {
"buildNum": 9007199254740991,
"defaultIndex": "logstash-*"
},
"type": "config",
"updated_at": "2019-01-22T19:32:02.235Z"
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -33,7 +33,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
kbnTestServer: {
...apiConfig.get('kbnTestServer'),
serverArgs: [
`--migrations.enableV2=false`,
`--elasticsearch.hosts=${formatUrl(esTestConfig.getUrlParts())}`,
`--logging.json=false`,
`--server.maxPayloadBytes=1679958`,