mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Migrations v2: don't auto-create indices + FTR/esArchiver support (#85778)
* Migrations V2 on by default * esArchiver delete migrations v2 indices * Fix saved_objects_management api_integration tests * Try to fix v2 migrations for pre-release builds * esArchiver delete auto-created v2 migration indices like .kibana_8.0.0 * Try to fix v2 migrations for pre-release builds * Use require_alias to prevent auto-created saved objects index * Wrap SO routes until core logs all internal errors * Fix api_integration tests requiring an empty kibana index * Delete corrupt saved object from lens archives * Update docs * Fix ui_settings tests * Fix core jest tests * Fix type errors * Fix accessibility tests * Fix plugin functional tests * Fix api_integration tests after merging in master * Fix plugin functional tests #2 * EsArchiver: Don't reset ui settings after the .kibana index was deleted * Fix functional management/visualize tests * Fix oss security functional tests * EsArchiver clean task manager indices to fix alerting api integration tests * migrationsv2 correctly handle unknown saved object type mappings * Revert "Try to fix v2 migrations for pre-release builds" This reverts commita1a1567501
. * Revert "Try to fix v2 migrations for pre-release builds" This reverts commita9a935558c
. * Re-enable v2 migrations in tests after merging in master * Try to fix async dashboard functional test * Restore UiSettings defaults after emptyKibanaIndex() * Review feedback: rename test to match behaviour
This commit is contained in:
parent
c66124e170
commit
03636a07fe
71 changed files with 645 additions and 238 deletions
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) > [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`
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) > [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`
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) > [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 | DecoratedError</code> | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
`boolean`
|
||||
|
|
@ -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> | |
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -76,7 +76,9 @@ export async function migrateKibanaIndex({
|
|||
*/
|
||||
async function fetchKibanaIndices(client: Client) {
|
||||
const kibanaIndices = await client.cat.indices({ 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);
|
||||
return kibanaIndices.map((x: { index: string }) => x.index).filter(isKibanaIndex);
|
||||
}
|
||||
|
||||
|
@ -103,7 +105,7 @@ export async function cleanKibanaIndices({
|
|||
|
||||
while (true) {
|
||||
const resp = await client.deleteByQuery({
|
||||
index: `.kibana`,
|
||||
index: `.kibana,.kibana_task_manager`,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
|
@ -115,7 +117,7 @@ export async function cleanKibanaIndices({
|
|||
},
|
||||
},
|
||||
},
|
||||
ignore: [409],
|
||||
ignore: [404, 409],
|
||||
});
|
||||
|
||||
if (resp.total !== resp.deleted) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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
|
||||
),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(() => {});
|
||||
|
||||
|
|
|
@ -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(() => {});
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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]`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -2336,6 +2336,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;
|
||||
|
@ -2354,6 +2356,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;
|
||||
|
@ -2370,6 +2374,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;
|
||||
|
|
|
@ -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() {
|
|||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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() {
|
|||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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() {
|
|||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,20 +6,25 @@
|
|||
* 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);
|
||||
});
|
||||
|
|
|
@ -37,9 +37,6 @@ export async function startServers() {
|
|||
adjustTimeout: (t) => jest.setTimeout(t),
|
||||
settings: {
|
||||
kbn: {
|
||||
migrations: {
|
||||
enableV2: false,
|
||||
},
|
||||
uiSettings: {
|
||||
overrides: {
|
||||
foo: 'bar',
|
||||
|
|
|
@ -40,7 +40,7 @@ const DEFAULTS_SETTINGS = {
|
|||
},
|
||||
logging: { silent: true },
|
||||
plugins: {},
|
||||
migrations: { skip: true },
|
||||
migrations: { skip: false },
|
||||
};
|
||||
|
||||
const DEFAULT_SETTINGS_WITH_CORE_PLUGINS = {
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -97,10 +97,11 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
],
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return 200 with individual responses', async () =>
|
||||
|
|
|
@ -235,10 +235,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
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',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -82,10 +82,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
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({
|
||||
|
@ -93,50 +93,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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('returns generic 404 when kibana index is missing', async () =>
|
||||
|
|
|
@ -534,7 +534,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return empty response', async () => {
|
||||
|
|
|
@ -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 es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return 200 with empty response', async () =>
|
||||
|
|
|
@ -78,7 +78,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return basic 404 without mentioning index', async () =>
|
||||
|
|
|
@ -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('legacyEs');
|
||||
|
||||
describe('resolve_import_errors', () => {
|
||||
// mock success results including metadata
|
||||
|
@ -34,7 +35,14 @@ 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 es.indices.delete({
|
||||
index: '.kibana*',
|
||||
ignore: [404],
|
||||
})
|
||||
);
|
||||
|
||||
it('should return 200 and import nothing when empty parameters are passed in', async () => {
|
||||
await supertest
|
||||
|
@ -51,7 +59,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 +86,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: [],
|
||||
});
|
||||
|
|
|
@ -121,10 +121,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
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.',
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
|
|
@ -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 es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return 200 with empty response', async () =>
|
||||
|
|
|
@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
before(
|
||||
async () =>
|
||||
// just in case the kibana server has recreated it
|
||||
await es.indices.delete({ index: '.kibana' }, { ignore: [404] })
|
||||
await es.indices.delete({ index: '.kibana*' }, { ignore: [404] })
|
||||
);
|
||||
|
||||
it('should return 404 for object that no longer exists', async () =>
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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) => ({
|
||||
|
@ -23,6 +24,10 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
describe('UI Counters API', () => {
|
||||
before(async () => {
|
||||
await esArchiver.emptyKibanaIndex();
|
||||
});
|
||||
|
||||
const dayDate = moment().format('DDMMYYYY');
|
||||
|
||||
it('stores ui counter events in savedObjects', async () => {
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* 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
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -11,10 +11,6 @@ import { setupSpacesAndUsers, tearDown } from '..';
|
|||
export default function alertingTests({ loadTestFile, getService }: FtrProviderContext) {
|
||||
describe('Alerts', () => {
|
||||
describe('legacy alerts', () => {
|
||||
before(async () => {
|
||||
await setupSpacesAndUsers(getService);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await tearDown(getService);
|
||||
});
|
||||
|
|
|
@ -12,6 +12,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');
|
||||
|
@ -29,6 +30,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,
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -32,7 +32,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`,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue