mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
Migrate Index Management and enrichers to the new ES JS client (#105863)
* Destructure index API request bodies consistently. * Remove unnecessary calls to encodeURIComponent on the server. * Migrate routes to handleEsError. Delete wrapEsError helpers. Remove unused isEsError and parseEsError dependencies. Remove isEsError from es_ui_shared. * Update tests and migrate API integration tests. * Clarify test details in CCR README. Update Index Management README with steps for testing Cloud-managed index templates and steps for testing indices and data streams that contain special characters.
This commit is contained in:
parent
5e8b24230a
commit
3491f05e95
55 changed files with 682 additions and 1135 deletions
|
@ -457,7 +457,7 @@ Index Management by running this series of requests in Console:
|
||||||
|
|
||||||
|
|
||||||
|{kib-repo}blob/{branch}/x-pack/plugins/index_management/README.md[indexManagement]
|
|{kib-repo}blob/{branch}/x-pack/plugins/index_management/README.md[indexManagement]
|
||||||
|Create a data stream using Console and you'll be able to view it in the UI:
|
|Create an index with special characters and verify it renders correctly:
|
||||||
|
|
||||||
|
|
||||||
|{kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra]
|
|{kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra]
|
||||||
|
|
|
@ -6,6 +6,5 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { isEsError } from './is_es_error';
|
|
||||||
export { handleEsError } from './handle_es_error';
|
export { handleEsError } from './handle_es_error';
|
||||||
export { parseEsError } from './es_error_parser';
|
export { parseEsError } from './es_error_parser';
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
|
||||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
|
||||||
* Side Public License, v 1.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as legacyElasticsearch from 'elasticsearch';
|
|
||||||
|
|
||||||
const esErrorsParent = legacyElasticsearch.errors._Abstract;
|
|
||||||
|
|
||||||
interface RequestError extends Error {
|
|
||||||
statusCode?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @deprecated
|
|
||||||
* Only works with legacy elasticsearch js client errors and will be removed after 7.x last
|
|
||||||
*/
|
|
||||||
export function isEsError(err: RequestError) {
|
|
||||||
const isInstanceOfEsError = err instanceof esErrorsParent;
|
|
||||||
const hasStatusCode = Boolean(err.statusCode);
|
|
||||||
|
|
||||||
return isInstanceOfEsError && hasStatusCode;
|
|
||||||
}
|
|
|
@ -6,4 +6,4 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { isEsError, handleEsError, parseEsError } from '../../__packages_do_not_import__/errors';
|
export { handleEsError, parseEsError } from '../../__packages_do_not_import__/errors';
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { isEsError, handleEsError, parseEsError } from './errors';
|
export { handleEsError, parseEsError } from './errors';
|
||||||
|
|
||||||
/** dummy plugin*/
|
/** dummy plugin*/
|
||||||
export function plugin() {
|
export function plugin() {
|
||||||
|
|
|
@ -4,14 +4,15 @@
|
||||||
|
|
||||||
You can run a local cluster and simulate a remote cluster within a single Kibana directory.
|
You can run a local cluster and simulate a remote cluster within a single Kibana directory.
|
||||||
|
|
||||||
1. Ensure Kibana isn't running so it doesn't load up any data into your cluster. Run `yarn es snapshot --license=trial` to install a fresh snapshot. Wait for ES to finish setting up.
|
1. Ensure Kibana isn't running so it doesn't load up any data into your cluster. Run `yarn es snapshot --license=trial` to install a fresh snapshot. Wait for ES to finish setting up and activate the license.
|
||||||
2. Create a "remote" copy of your ES snapshot by running: `cp -R .es/8.0.0 .es/8.0.0-2`.
|
2. Create a "remote" copy of your ES snapshot by running: `cp -R .es/8.0.0 .es/8.0.0-2`.
|
||||||
4. Start your "remote" cluster by running `.es/8.0.0-2/bin/elasticsearch -E cluster.name=europe -E transport.port=9400`.
|
4. Start your "local" cluster by running `.es/8.0.0/bin/elasticsearch`.
|
||||||
4. Run `yarn start` to start Kibana.
|
5. Run `yarn start` to start Kibana so that it connects to the "local" cluster.
|
||||||
5. Index a document into your remote cluster by running `curl -X PUT http://elastic:changeme@localhost:9201/my-leader-index --data '{"settings":{"number_of_shards":1,"soft_deletes.enabled":true}}' --header "Content-Type: application/json"`. Note that these settings are required for testing auto-follow pattern conflicts errors (see below).
|
6. Start your "remote" cluster by running `.es/8.0.0-2/bin/elasticsearch -E cluster.name=europe -E transport.port=9400`.
|
||||||
|
7. Index a document into your "remote" cluster by running `curl -X PUT http://elastic:changeme@localhost:9201/my-leader-index --data '{"settings":{"number_of_shards":1,"soft_deletes.enabled":true}}' --header "Content-Type: application/json"`. Note that these settings are required for testing auto-follow pattern conflicts errors (see below).
|
||||||
|
|
||||||
Now you can create follower indices and auto-follow patterns to replicate the `my-leader-index`
|
Now you can create follower indices and auto-follow patterns to replicate the `my-leader-index`
|
||||||
index on the remote cluster that's available at `127.0.0.1:9400`.
|
index on the "remote" cluster that's available at `127.0.0.1:9400`.
|
||||||
|
|
||||||
### Auto-follow pattern conflict errors
|
### Auto-follow pattern conflict errors
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,8 @@
|
||||||
|
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
import {
|
import { CoreSetup, CoreStart, Plugin, Logger, PluginInitializerContext } from 'src/core/server';
|
||||||
CoreSetup,
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
CoreStart,
|
|
||||||
Plugin,
|
|
||||||
Logger,
|
|
||||||
PluginInitializerContext,
|
|
||||||
LegacyAPICaller,
|
|
||||||
} from 'src/core/server';
|
|
||||||
|
|
||||||
import { Index } from '../../index_management/server';
|
import { Index } from '../../index_management/server';
|
||||||
import { PLUGIN } from '../common/constants';
|
import { PLUGIN } from '../common/constants';
|
||||||
|
@ -23,20 +17,18 @@ import { registerApiRoutes } from './routes';
|
||||||
import { CrossClusterReplicationConfig } from './config';
|
import { CrossClusterReplicationConfig } from './config';
|
||||||
import { License, handleEsError } from './shared_imports';
|
import { License, handleEsError } from './shared_imports';
|
||||||
|
|
||||||
// TODO replace deprecated ES client after Index Management is updated
|
const ccrDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => {
|
||||||
const ccrDataEnricher = async (indicesList: Index[], callWithRequest: LegacyAPICaller) => {
|
|
||||||
if (!indicesList?.length) {
|
if (!indicesList?.length) {
|
||||||
return indicesList;
|
return indicesList;
|
||||||
}
|
}
|
||||||
const params = {
|
|
||||||
path: '/_all/_ccr/info',
|
|
||||||
method: 'GET',
|
|
||||||
};
|
|
||||||
try {
|
try {
|
||||||
const { follower_indices: followerIndices } = await callWithRequest(
|
const {
|
||||||
'transport.request',
|
body: { follower_indices: followerIndices },
|
||||||
params
|
} = await client.asCurrentUser.ccr.followInfo({
|
||||||
);
|
index: '_all',
|
||||||
|
});
|
||||||
|
|
||||||
return indicesList.map((index) => {
|
return indicesList.map((index) => {
|
||||||
const isFollowerIndex = !!followerIndices.find(
|
const isFollowerIndex = !!followerIndices.find(
|
||||||
(followerIndex: { follower_index: string }) => {
|
(followerIndex: { follower_index: string }) => {
|
||||||
|
|
|
@ -6,44 +6,36 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import {
|
import { CoreSetup, Plugin, Logger, PluginInitializerContext } from 'src/core/server';
|
||||||
CoreSetup,
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
Plugin,
|
|
||||||
Logger,
|
|
||||||
PluginInitializerContext,
|
|
||||||
LegacyAPICaller,
|
|
||||||
} from 'src/core/server';
|
|
||||||
import { handleEsError } from './shared_imports';
|
|
||||||
|
|
||||||
import { Index as IndexWithoutIlm } from '../../index_management/common/types';
|
import { Index as IndexWithoutIlm } from '../../index_management/common/types';
|
||||||
import { PLUGIN } from '../common/constants';
|
import { PLUGIN } from '../common/constants';
|
||||||
import { Index, IndexLifecyclePolicy } from '../common/types';
|
import { Index } from '../common/types';
|
||||||
import { Dependencies } from './types';
|
import { Dependencies } from './types';
|
||||||
import { registerApiRoutes } from './routes';
|
import { registerApiRoutes } from './routes';
|
||||||
import { License } from './services';
|
import { License } from './services';
|
||||||
import { IndexLifecycleManagementConfig } from './config';
|
import { IndexLifecycleManagementConfig } from './config';
|
||||||
|
import { handleEsError } from './shared_imports';
|
||||||
|
|
||||||
const indexLifecycleDataEnricher = async (
|
const indexLifecycleDataEnricher = async (
|
||||||
indicesList: IndexWithoutIlm[],
|
indicesList: IndexWithoutIlm[],
|
||||||
// TODO replace deprecated ES client after Index Management is updated
|
client: IScopedClusterClient
|
||||||
callAsCurrentUser: LegacyAPICaller
|
|
||||||
): Promise<Index[]> => {
|
): Promise<Index[]> => {
|
||||||
if (!indicesList || !indicesList.length) {
|
if (!indicesList || !indicesList.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
const {
|
||||||
path: '/*/_ilm/explain',
|
body: { indices: ilmIndicesData },
|
||||||
method: 'GET',
|
} = await client.asCurrentUser.ilm.explainLifecycle({
|
||||||
};
|
index: '*',
|
||||||
|
});
|
||||||
const { indices: ilmIndicesData } = await callAsCurrentUser<{
|
|
||||||
indices: { [indexName: string]: IndexLifecyclePolicy };
|
|
||||||
}>('transport.request', params);
|
|
||||||
|
|
||||||
return indicesList.map((index: IndexWithoutIlm) => {
|
return indicesList.map((index: IndexWithoutIlm) => {
|
||||||
return {
|
return {
|
||||||
...index,
|
...index,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch Element implicitly has an 'any' type
|
||||||
ilm: { ...(ilmIndicesData[index.name] || {}) },
|
ilm: { ...(ilmIndicesData[index.name] || {}) },
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,16 @@
|
||||||
# Index Management UI
|
# Index Management UI
|
||||||
|
|
||||||
|
## Indices tab
|
||||||
|
|
||||||
|
### Quick steps for testing
|
||||||
|
|
||||||
|
Create an index with special characters and verify it renders correctly:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Renders as %{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23
|
||||||
|
PUT %25%7B%5B%40metadata%5D%5Bbeat%5D%7D-%25%7B%5B%40metadata%5D%5Bversion%5D%7D-2020.08.23
|
||||||
|
```
|
||||||
|
|
||||||
## Data streams tab
|
## Data streams tab
|
||||||
|
|
||||||
### Quick steps for testing
|
### Quick steps for testing
|
||||||
|
@ -20,3 +31,55 @@ POST ds/_doc
|
||||||
"@timestamp": "2020-01-27"
|
"@timestamp": "2020-01-27"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create a data stream with special characters and verify it renders correctly:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Configure template for creating a data stream
|
||||||
|
PUT _index_template/special_ds
|
||||||
|
{
|
||||||
|
"index_patterns": ["%{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23"],
|
||||||
|
"data_stream": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add a document to the data stream, which will render as %{[@metadata][beat]}-%{[@metadata][version]}-2020.08.23
|
||||||
|
POST %25%7B%5B%40metadata%5D%5Bbeat%5D%7D-%25%7B%5B%40metadata%5D%5Bversion%5D%7D-2020.08.23/_doc
|
||||||
|
{
|
||||||
|
"@timestamp": "2020-01-27"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Index templates tab
|
||||||
|
|
||||||
|
### Quick steps for testing
|
||||||
|
|
||||||
|
By default, **legacy index templates** are not shown in the UI. Make them appear by creating one in Console:
|
||||||
|
|
||||||
|
```
|
||||||
|
PUT _template/template_1
|
||||||
|
{
|
||||||
|
"index_patterns": ["foo*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To test **Cloud-managed templates**:
|
||||||
|
|
||||||
|
1. Add `cluster.metadata.managed_index_templates` setting via Dev Tools:
|
||||||
|
```
|
||||||
|
PUT /_cluster/settings
|
||||||
|
{
|
||||||
|
"persistent": {
|
||||||
|
"cluster.metadata.managed_index_templates": ".cloud-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Create a template with the format: `.cloud-<template_name>` via Dev Tools.
|
||||||
|
```
|
||||||
|
PUT _template/.cloud-example
|
||||||
|
{
|
||||||
|
"index_patterns": [ "foobar*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The UI will now prevent you from editing or deleting this template.
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect } from 'react';
|
import React, { useState, useCallback, useEffect } from 'react';
|
||||||
import uuid from 'uuid';
|
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
|
import { EuiCodeBlock, EuiCallOut } from '@elastic/eui';
|
||||||
|
|
||||||
|
@ -37,11 +36,6 @@ export const SimulateTemplate = React.memo(({ template, filters }: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexTemplate = serializeTemplate(stripEmptyFields(template) as TemplateDeserialized);
|
const indexTemplate = serializeTemplate(stripEmptyFields(template) as TemplateDeserialized);
|
||||||
|
|
||||||
// Until ES fixes a bug on their side we will send a random index pattern to the simulate API.
|
|
||||||
// Issue: https://github.com/elastic/elasticsearch/issues/59152
|
|
||||||
indexTemplate.index_patterns = [uuid.v4()];
|
|
||||||
|
|
||||||
const { data, error } = await simulateIndexTemplate(indexTemplate);
|
const { data, error } = await simulateIndexTemplate(indexTemplate);
|
||||||
let filteredTemplate = data;
|
let filteredTemplate = data;
|
||||||
|
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const elasticsearchJsPlugin = (Client: any, config: any, components: any) => {
|
|
||||||
const ca = components.clientAction.factory;
|
|
||||||
|
|
||||||
Client.prototype.dataManagement = components.clientAction.namespaceFactory();
|
|
||||||
const dataManagement = Client.prototype.dataManagement.prototype;
|
|
||||||
|
|
||||||
// Data streams
|
|
||||||
|
|
||||||
// We don't allow the user to create a data stream in the UI or API. We're just adding this here
|
|
||||||
// to enable the API integration tests.
|
|
||||||
dataManagement.createDataStream = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_stream/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'PUT',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.deleteDataStream = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_data_stream/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Component templates
|
|
||||||
dataManagement.getComponentTemplates = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_component_template',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.getComponentTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_component_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.saveComponentTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_component_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'PUT',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.deleteComponentTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_component_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
|
|
||||||
// Composable index templates
|
|
||||||
dataManagement.getComposableIndexTemplates = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.getComposableIndexTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.saveComposableIndexTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
needBody: true,
|
|
||||||
method: 'PUT',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.deleteComposableIndexTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.existsTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template/<%=name%>',
|
|
||||||
req: {
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
method: 'HEAD',
|
|
||||||
});
|
|
||||||
|
|
||||||
dataManagement.simulateTemplate = ca({
|
|
||||||
urls: [
|
|
||||||
{
|
|
||||||
fmt: '/_index_template/_simulate',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
needBody: true,
|
|
||||||
method: 'POST',
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -10,7 +10,7 @@ import { PluginInitializerContext } from 'src/core/server';
|
||||||
import { IndexMgmtServerPlugin } from './plugin';
|
import { IndexMgmtServerPlugin } from './plugin';
|
||||||
import { configSchema } from './config';
|
import { configSchema } from './config';
|
||||||
|
|
||||||
export const plugin = (ctx: PluginInitializerContext) => new IndexMgmtServerPlugin(ctx);
|
export const plugin = (context: PluginInitializerContext) => new IndexMgmtServerPlugin(context);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
schema: configSchema,
|
schema: configSchema,
|
||||||
|
|
|
@ -5,99 +5,57 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CatIndicesParams } from 'elasticsearch';
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
import { IndexDataEnricher } from '../services';
|
import { IndexDataEnricher } from '../services';
|
||||||
import { CallAsCurrentUser } from '../types';
|
|
||||||
import { Index } from '../index';
|
import { Index } from '../index';
|
||||||
|
|
||||||
interface Hit {
|
|
||||||
health: string;
|
|
||||||
status: string;
|
|
||||||
index: string;
|
|
||||||
uuid: string;
|
|
||||||
pri: string;
|
|
||||||
rep: string;
|
|
||||||
'docs.count': any;
|
|
||||||
'store.size': any;
|
|
||||||
sth: 'true' | 'false';
|
|
||||||
hidden: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IndexInfo {
|
|
||||||
aliases: { [aliasName: string]: unknown };
|
|
||||||
mappings: unknown;
|
|
||||||
data_stream?: string;
|
|
||||||
settings: {
|
|
||||||
index: {
|
|
||||||
hidden: 'true' | 'false';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetIndicesResponse {
|
|
||||||
[indexName: string]: IndexInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchIndicesCall(
|
async function fetchIndicesCall(
|
||||||
callAsCurrentUser: CallAsCurrentUser,
|
client: IScopedClusterClient,
|
||||||
indexNames?: string[]
|
indexNames?: string[]
|
||||||
): Promise<Index[]> {
|
): Promise<Index[]> {
|
||||||
const indexNamesString = indexNames && indexNames.length ? indexNames.join(',') : '*';
|
const indexNamesString = indexNames && indexNames.length ? indexNames.join(',') : '*';
|
||||||
|
|
||||||
// This call retrieves alias and settings (incl. hidden status) information about indices
|
// This call retrieves alias and settings (incl. hidden status) information about indices
|
||||||
const indices: GetIndicesResponse = await callAsCurrentUser('transport.request', {
|
const { body: indices } = await client.asCurrentUser.indices.get({
|
||||||
method: 'GET',
|
index: indexNamesString,
|
||||||
// transport.request doesn't do any URI encoding, unlike other JS client APIs. This enables
|
expand_wildcards: 'hidden,all',
|
||||||
// working with Logstash indices with names like %{[@metadata][beat]}-%{[@metadata][version]}.
|
|
||||||
path: `/${encodeURIComponent(indexNamesString)}`,
|
|
||||||
query: {
|
|
||||||
expand_wildcards: 'hidden,all',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!Object.keys(indices).length) {
|
if (!Object.keys(indices).length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const catQuery: Pick<CatIndicesParams, 'format' | 'h'> & {
|
const { body: catHits } = await client.asCurrentUser.cat.indices({
|
||||||
expand_wildcards: string;
|
|
||||||
index?: string;
|
|
||||||
} = {
|
|
||||||
format: 'json',
|
format: 'json',
|
||||||
h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size',
|
h: 'health,status,index,uuid,pri,rep,docs.count,sth,store.size',
|
||||||
expand_wildcards: 'hidden,all',
|
expand_wildcards: 'hidden,all',
|
||||||
index: indexNamesString,
|
index: indexNamesString,
|
||||||
};
|
|
||||||
|
|
||||||
// This call retrieves health and other high-level information about indices.
|
|
||||||
const catHits: Hit[] = await callAsCurrentUser('transport.request', {
|
|
||||||
method: 'GET',
|
|
||||||
path: '/_cat/indices',
|
|
||||||
query: catQuery,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// System indices may show up in _cat APIs, as these APIs are primarily used for troubleshooting
|
// System indices may show up in _cat APIs, as these APIs are primarily used for troubleshooting
|
||||||
// For now, we filter them out and only return index information for the indices we have
|
// For now, we filter them out and only return index information for the indices we have
|
||||||
// In the future, we should migrate away from using cat APIs (https://github.com/elastic/kibana/issues/57286)
|
// In the future, we should migrate away from using cat APIs (https://github.com/elastic/kibana/issues/57286)
|
||||||
return catHits.reduce((decoratedIndices, hit) => {
|
return catHits.reduce((decoratedIndices, hit) => {
|
||||||
const index = indices[hit.index];
|
const index = indices[hit.index!];
|
||||||
|
|
||||||
if (typeof index !== 'undefined') {
|
if (typeof index !== 'undefined') {
|
||||||
const aliases = Object.keys(index.aliases);
|
const aliases = Object.keys(index.aliases!);
|
||||||
|
|
||||||
decoratedIndices.push({
|
decoratedIndices.push({
|
||||||
health: hit.health,
|
health: hit.health!,
|
||||||
status: hit.status,
|
status: hit.status!,
|
||||||
name: hit.index,
|
name: hit.index!,
|
||||||
uuid: hit.uuid,
|
uuid: hit.uuid!,
|
||||||
primary: hit.pri,
|
primary: hit.pri!,
|
||||||
replica: hit.rep,
|
replica: hit.rep!,
|
||||||
documents: hit['docs.count'],
|
documents: hit['docs.count'],
|
||||||
size: hit['store.size'],
|
size: hit['store.size'],
|
||||||
isFrozen: hit.sth === 'true', // sth value coming back as a string from ES
|
isFrozen: hit.sth === 'true', // sth value coming back as a string from ES
|
||||||
aliases: aliases.length ? aliases : 'none',
|
aliases: aliases.length ? aliases : 'none',
|
||||||
|
// @ts-expect-error @elastic/elasticsearch Property 'index' does not exist on type 'IndicesIndexSettings | IndicesIndexStatePrefixedSettings'.
|
||||||
hidden: index.settings.index.hidden === 'true',
|
hidden: index.settings.index.hidden === 'true',
|
||||||
data_stream: index.data_stream,
|
// @ts-expect-error @elastic/elasticsearch Property 'data_stream' does not exist on type 'IndicesIndexState'.
|
||||||
|
data_stream: index.data_stream!,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,10 +64,10 @@ async function fetchIndicesCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchIndices = async (
|
export const fetchIndices = async (
|
||||||
callAsCurrentUser: CallAsCurrentUser,
|
client: IScopedClusterClient,
|
||||||
indexDataEnricher: IndexDataEnricher,
|
indexDataEnricher: IndexDataEnricher,
|
||||||
indexNames?: string[]
|
indexNames?: string[]
|
||||||
) => {
|
) => {
|
||||||
const indices = await fetchIndicesCall(callAsCurrentUser, indexNames);
|
const indices = await fetchIndicesCall(client, indexNames);
|
||||||
return await indexDataEnricher.enrichIndices(indices, callAsCurrentUser);
|
return await indexDataEnricher.enrichIndices(indices, client);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,16 +5,20 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
|
|
||||||
// Cloud has its own system for managing templates and we want to make
|
// Cloud has its own system for managing templates and we want to make
|
||||||
// this clear in the UI when a template is used in a Cloud deployment.
|
// this clear in the UI when a template is used in a Cloud deployment.
|
||||||
export const getCloudManagedTemplatePrefix = async (
|
export const getCloudManagedTemplatePrefix = async (
|
||||||
callAsCurrentUser: any
|
client: IScopedClusterClient
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
try {
|
try {
|
||||||
const { persistent, transient, defaults } = await callAsCurrentUser('cluster.getSettings', {
|
const {
|
||||||
filterPath: '*.*managed_index_templates',
|
body: { persistent, transient, defaults },
|
||||||
flatSettings: true,
|
} = await client.asCurrentUser.cluster.getSettings({
|
||||||
includeDefaults: true,
|
filter_path: '*.*managed_index_templates',
|
||||||
|
flat_settings: true,
|
||||||
|
include_defaults: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { 'cluster.metadata.managed_index_templates': managedTemplatesPrefix = undefined } = {
|
const { 'cluster.metadata.managed_index_templates': managedTemplatesPrefix = undefined } = {
|
||||||
|
|
|
@ -5,20 +5,13 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { CoreSetup, Plugin, PluginInitializerContext } from 'src/core/server';
|
||||||
CoreSetup,
|
|
||||||
Plugin,
|
|
||||||
PluginInitializerContext,
|
|
||||||
ILegacyCustomClusterClient,
|
|
||||||
} from 'src/core/server';
|
|
||||||
|
|
||||||
import { PLUGIN } from '../common/constants/plugin';
|
import { PLUGIN } from '../common/constants/plugin';
|
||||||
import { Dependencies } from './types';
|
import { Dependencies } from './types';
|
||||||
import { ApiRoutes } from './routes';
|
import { ApiRoutes } from './routes';
|
||||||
import { IndexDataEnricher } from './services';
|
import { IndexDataEnricher } from './services';
|
||||||
import { isEsError, handleEsError, parseEsError } from './shared_imports';
|
import { handleEsError } from './shared_imports';
|
||||||
import { elasticsearchJsPlugin } from './client/elasticsearch';
|
|
||||||
import type { IndexManagementRequestHandlerContext } from './types';
|
|
||||||
|
|
||||||
export interface IndexManagementPluginSetup {
|
export interface IndexManagementPluginSetup {
|
||||||
indexDataEnricher: {
|
indexDataEnricher: {
|
||||||
|
@ -26,16 +19,9 @@ export interface IndexManagementPluginSetup {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) {
|
|
||||||
const [core] = await getStartServices();
|
|
||||||
const esClientConfig = { plugins: [elasticsearchJsPlugin] };
|
|
||||||
return core.elasticsearch.legacy.createClient('dataManagement', esClientConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup, void, any, any> {
|
export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup, void, any, any> {
|
||||||
private readonly apiRoutes: ApiRoutes;
|
private readonly apiRoutes: ApiRoutes;
|
||||||
private readonly indexDataEnricher: IndexDataEnricher;
|
private readonly indexDataEnricher: IndexDataEnricher;
|
||||||
private dataManagementESClient?: ILegacyCustomClusterClient;
|
|
||||||
|
|
||||||
constructor(initContext: PluginInitializerContext) {
|
constructor(initContext: PluginInitializerContext) {
|
||||||
this.apiRoutes = new ApiRoutes();
|
this.apiRoutes = new ApiRoutes();
|
||||||
|
@ -46,8 +32,6 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
||||||
{ http, getStartServices }: CoreSetup,
|
{ http, getStartServices }: CoreSetup,
|
||||||
{ features, security }: Dependencies
|
{ features, security }: Dependencies
|
||||||
): IndexManagementPluginSetup {
|
): IndexManagementPluginSetup {
|
||||||
const router = http.createRouter<IndexManagementRequestHandlerContext>();
|
|
||||||
|
|
||||||
features.registerElasticsearchFeature({
|
features.registerElasticsearchFeature({
|
||||||
id: PLUGIN.id,
|
id: PLUGIN.id,
|
||||||
management: {
|
management: {
|
||||||
|
@ -63,27 +47,13 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
http.registerRouteHandlerContext<IndexManagementRequestHandlerContext, 'dataManagement'>(
|
|
||||||
'dataManagement',
|
|
||||||
async (ctx, request) => {
|
|
||||||
this.dataManagementESClient =
|
|
||||||
this.dataManagementESClient ?? (await getCustomEsClient(getStartServices));
|
|
||||||
|
|
||||||
return {
|
|
||||||
client: this.dataManagementESClient.asScoped(request),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.apiRoutes.setup({
|
this.apiRoutes.setup({
|
||||||
router,
|
router: http.createRouter(),
|
||||||
config: {
|
config: {
|
||||||
isSecurityEnabled: () => security !== undefined && security.license.isEnabled(),
|
isSecurityEnabled: () => security !== undefined && security.license.isEnabled(),
|
||||||
},
|
},
|
||||||
indexDataEnricher: this.indexDataEnricher,
|
indexDataEnricher: this.indexDataEnricher,
|
||||||
lib: {
|
lib: {
|
||||||
isEsError,
|
|
||||||
parseEsError,
|
|
||||||
handleEsError,
|
handleEsError,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -97,9 +67,5 @@ export class IndexMgmtServerPlugin implements Plugin<IndexManagementPluginSetup,
|
||||||
|
|
||||||
start() {}
|
start() {}
|
||||||
|
|
||||||
stop() {
|
stop() {}
|
||||||
if (this.dataManagementESClient) {
|
|
||||||
this.dataManagementESClient.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,10 @@ import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
import { componentTemplateSchema } from './schema_validation';
|
import { componentTemplateSchema } from './schema_validation';
|
||||||
|
|
||||||
export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDependencies): void => {
|
export const registerCreateRoute = ({
|
||||||
|
router,
|
||||||
|
lib: { handleEsError },
|
||||||
|
}: RouteDependencies): void => {
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: addBasePath('/component_templates'),
|
path: addBasePath('/component_templates'),
|
||||||
|
@ -20,24 +23,23 @@ export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
||||||
body: componentTemplateSchema,
|
body: componentTemplateSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
|
|
||||||
const serializedComponentTemplate = serializeComponentTemplate(req.body);
|
const serializedComponentTemplate = serializeComponentTemplate(request.body);
|
||||||
|
|
||||||
const { name } = req.body;
|
const { name } = request.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check that a component template with the same name doesn't already exist
|
// Check that a component template with the same name doesn't already exist
|
||||||
const componentTemplateResponse = await callAsCurrentUser(
|
const {
|
||||||
'dataManagement.getComponentTemplate',
|
body: { component_templates: componentTemplates },
|
||||||
{ name }
|
} = await client.asCurrentUser.cluster.getComponentTemplate({
|
||||||
);
|
name,
|
||||||
|
});
|
||||||
const { component_templates: componentTemplates } = componentTemplateResponse;
|
|
||||||
|
|
||||||
if (componentTemplates.length) {
|
if (componentTemplates.length) {
|
||||||
return res.conflict({
|
return response.conflict({
|
||||||
body: new Error(
|
body: new Error(
|
||||||
i18n.translate('xpack.idxMgmt.componentTemplates.createRoute.duplicateErrorMessage', {
|
i18n.translate('xpack.idxMgmt.componentTemplates.createRoute.duplicateErrorMessage', {
|
||||||
defaultMessage: "There is already a component template with name '{name}'.",
|
defaultMessage: "There is already a component template with name '{name}'.",
|
||||||
|
@ -53,21 +55,15 @@ export const registerCreateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await callAsCurrentUser('dataManagement.saveComponentTemplate', {
|
const { body: responseBody } = await client.asCurrentUser.cluster.putComponentTemplate({
|
||||||
name,
|
name,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch Type 'ComponentTemplateSerialized' is not assignable
|
||||||
body: serializedComponentTemplate,
|
body: serializedComponentTemplate,
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok({ body: response });
|
return response.ok({ body: responseBody });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isEsError(error)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: error.statusCode,
|
|
||||||
body: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,7 +14,10 @@ const paramsSchema = schema.object({
|
||||||
names: schema.string(),
|
names: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const registerDeleteRoute = ({ router }: RouteDependencies): void => {
|
export const registerDeleteRoute = ({
|
||||||
|
router,
|
||||||
|
lib: { handleEsError },
|
||||||
|
}: RouteDependencies): void => {
|
||||||
router.delete(
|
router.delete(
|
||||||
{
|
{
|
||||||
path: addBasePath('/component_templates/{names}'),
|
path: addBasePath('/component_templates/{names}'),
|
||||||
|
@ -22,32 +25,34 @@ export const registerDeleteRoute = ({ router }: RouteDependencies): void => {
|
||||||
params: paramsSchema,
|
params: paramsSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { names } = req.params;
|
const { names } = request.params;
|
||||||
const componentNames = names.split(',');
|
const componentNames = names.split(',');
|
||||||
|
|
||||||
const response: { itemsDeleted: string[]; errors: any[] } = {
|
const responseBody: { itemsDeleted: string[]; errors: any[] } = {
|
||||||
itemsDeleted: [],
|
itemsDeleted: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
componentNames.map((componentName) => {
|
componentNames.map(async (componentName) => {
|
||||||
return callAsCurrentUser('dataManagement.deleteComponentTemplate', {
|
try {
|
||||||
name: componentName,
|
await client.asCurrentUser.cluster.deleteComponentTemplate({
|
||||||
})
|
name: componentName,
|
||||||
.then(() => response.itemsDeleted.push(componentName))
|
});
|
||||||
.catch((e) =>
|
|
||||||
response.errors.push({
|
return responseBody.itemsDeleted.push(componentName);
|
||||||
name: componentName,
|
} catch (error) {
|
||||||
error: e,
|
return responseBody.errors.push({
|
||||||
})
|
name: componentName,
|
||||||
);
|
error: handleEsError({ error, response }),
|
||||||
|
});
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.ok({ body: response });
|
return response.ok({ body: responseBody });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,25 +19,23 @@ const paramsSchema = schema.object({
|
||||||
name: schema.string(),
|
name: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDependencies) {
|
export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
// Get all component templates
|
// Get all component templates
|
||||||
router.get(
|
router.get(
|
||||||
{ path: addBasePath('/component_templates'), validate: false },
|
{ path: addBasePath('/component_templates'), validate: false },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
component_templates: componentTemplates,
|
body: { component_templates: componentTemplates },
|
||||||
}: { component_templates: ComponentTemplateFromEs[] } = await callAsCurrentUser(
|
} = await client.asCurrentUser.cluster.getComponentTemplate();
|
||||||
'dataManagement.getComponentTemplates'
|
|
||||||
);
|
|
||||||
|
|
||||||
const { index_templates: indexTemplates } = await callAsCurrentUser(
|
const {
|
||||||
'dataManagement.getComposableIndexTemplates'
|
body: { index_templates: indexTemplates },
|
||||||
);
|
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||||
|
|
||||||
const body = componentTemplates.map((componentTemplate) => {
|
const body = componentTemplates.map((componentTemplate: ComponentTemplateFromEs) => {
|
||||||
const deserializedComponentTemplateListItem = deserializeComponentTemplateList(
|
const deserializedComponentTemplateListItem = deserializeComponentTemplateList(
|
||||||
componentTemplate,
|
componentTemplate,
|
||||||
indexTemplates
|
indexTemplates
|
||||||
|
@ -45,16 +43,9 @@ export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDepende
|
||||||
return deserializedComponentTemplateListItem;
|
return deserializedComponentTemplateListItem;
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok({ body });
|
return response.ok({ body });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isEsError(error)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: error.statusCode,
|
|
||||||
body: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -67,34 +58,26 @@ export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDepende
|
||||||
params: paramsSchema,
|
params: paramsSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { name } = req.params;
|
const { name } = request.params;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { component_templates: componentTemplates } = await callAsCurrentUser(
|
const {
|
||||||
'dataManagement.getComponentTemplates',
|
body: { component_templates: componentTemplates },
|
||||||
{
|
} = await client.asCurrentUser.cluster.getComponentTemplate({
|
||||||
name,
|
name,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { index_templates: indexTemplates } = await callAsCurrentUser(
|
const {
|
||||||
'dataManagement.getComposableIndexTemplates'
|
body: { index_templates: indexTemplates },
|
||||||
);
|
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||||
|
|
||||||
return res.ok({
|
return response.ok({
|
||||||
body: deserializeComponentTemplate(componentTemplates[0], indexTemplates),
|
body: deserializeComponentTemplate(componentTemplates[0], indexTemplates),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isEsError(error)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: error.statusCode,
|
|
||||||
body: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,17 +18,15 @@ const httpService = httpServiceMock.createSetupContract();
|
||||||
|
|
||||||
const mockedIndexDataEnricher = new IndexDataEnricher();
|
const mockedIndexDataEnricher = new IndexDataEnricher();
|
||||||
|
|
||||||
const mockRouteContext = ({
|
const mockRouteContext = ({ hasPrivileges }: { hasPrivileges: unknown }): RequestHandlerContext => {
|
||||||
callAsCurrentUser,
|
|
||||||
}: {
|
|
||||||
callAsCurrentUser: any;
|
|
||||||
}): RequestHandlerContext => {
|
|
||||||
const routeContextMock = ({
|
const routeContextMock = ({
|
||||||
core: {
|
core: {
|
||||||
elasticsearch: {
|
elasticsearch: {
|
||||||
legacy: {
|
client: {
|
||||||
client: {
|
asCurrentUser: {
|
||||||
callAsCurrentUser,
|
security: {
|
||||||
|
hasPrivileges,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -51,8 +49,6 @@ describe('GET privileges', () => {
|
||||||
},
|
},
|
||||||
indexDataEnricher: mockedIndexDataEnricher,
|
indexDataEnricher: mockedIndexDataEnricher,
|
||||||
lib: {
|
lib: {
|
||||||
isEsError: jest.fn(),
|
|
||||||
parseEsError: jest.fn(),
|
|
||||||
handleEsError: jest.fn(),
|
handleEsError: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -62,15 +58,17 @@ describe('GET privileges', () => {
|
||||||
|
|
||||||
it('should return the correct response when a user has privileges', async () => {
|
it('should return the correct response when a user has privileges', async () => {
|
||||||
const privilegesResponseMock = {
|
const privilegesResponseMock = {
|
||||||
username: 'elastic',
|
body: {
|
||||||
has_all_requested: true,
|
username: 'elastic',
|
||||||
cluster: { manage_index_templates: true },
|
has_all_requested: true,
|
||||||
index: {},
|
cluster: { manage_index_templates: true },
|
||||||
application: {},
|
index: {},
|
||||||
|
application: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const routeContextMock = mockRouteContext({
|
const routeContextMock = mockRouteContext({
|
||||||
callAsCurrentUser: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||||
});
|
});
|
||||||
|
|
||||||
const request = httpServerMock.createKibanaRequest();
|
const request = httpServerMock.createKibanaRequest();
|
||||||
|
@ -86,15 +84,17 @@ describe('GET privileges', () => {
|
||||||
|
|
||||||
it('should return the correct response when a user does not have privileges', async () => {
|
it('should return the correct response when a user does not have privileges', async () => {
|
||||||
const privilegesResponseMock = {
|
const privilegesResponseMock = {
|
||||||
username: 'elastic',
|
body: {
|
||||||
has_all_requested: false,
|
username: 'elastic',
|
||||||
cluster: { manage_index_templates: false },
|
has_all_requested: false,
|
||||||
index: {},
|
cluster: { manage_index_templates: false },
|
||||||
application: {},
|
index: {},
|
||||||
|
application: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const routeContextMock = mockRouteContext({
|
const routeContextMock = mockRouteContext({
|
||||||
callAsCurrentUser: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock),
|
||||||
});
|
});
|
||||||
|
|
||||||
const request = httpServerMock.createKibanaRequest();
|
const request = httpServerMock.createKibanaRequest();
|
||||||
|
@ -119,8 +119,6 @@ describe('GET privileges', () => {
|
||||||
},
|
},
|
||||||
indexDataEnricher: mockedIndexDataEnricher,
|
indexDataEnricher: mockedIndexDataEnricher,
|
||||||
lib: {
|
lib: {
|
||||||
isEsError: jest.fn(),
|
|
||||||
parseEsError: jest.fn(),
|
|
||||||
handleEsError: jest.fn(),
|
handleEsError: jest.fn(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -130,7 +128,7 @@ describe('GET privileges', () => {
|
||||||
|
|
||||||
it('should return the default privileges response', async () => {
|
it('should return the default privileges response', async () => {
|
||||||
const routeContextMock = mockRouteContext({
|
const routeContextMock = mockRouteContext({
|
||||||
callAsCurrentUser: jest.fn(),
|
hasPrivileges: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const request = httpServerMock.createKibanaRequest();
|
const request = httpServerMock.createKibanaRequest();
|
||||||
|
|
|
@ -17,13 +17,17 @@ const extractMissingPrivileges = (privilegesObject: { [key: string]: boolean } =
|
||||||
return privileges;
|
return privileges;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) => {
|
export const registerPrivilegesRoute = ({
|
||||||
|
router,
|
||||||
|
config,
|
||||||
|
lib: { handleEsError },
|
||||||
|
}: RouteDependencies) => {
|
||||||
router.get(
|
router.get(
|
||||||
{
|
{
|
||||||
path: addBasePath('/component_templates/privileges'),
|
path: addBasePath('/component_templates/privileges'),
|
||||||
validate: false,
|
validate: false,
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const privilegesResult: Privileges = {
|
const privilegesResult: Privileges = {
|
||||||
hasAllPrivileges: true,
|
hasAllPrivileges: true,
|
||||||
missingPrivileges: {
|
missingPrivileges: {
|
||||||
|
@ -33,38 +37,28 @@ export const registerPrivilegesRoute = ({ router, config }: RouteDependencies) =
|
||||||
|
|
||||||
// Skip the privileges check if security is not enabled
|
// Skip the privileges check if security is not enabled
|
||||||
if (!config.isSecurityEnabled()) {
|
if (!config.isSecurityEnabled()) {
|
||||||
return res.ok({ body: privilegesResult });
|
return response.ok({ body: privilegesResult });
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { client } = context.core.elasticsearch;
|
||||||
core: {
|
|
||||||
elasticsearch: {
|
|
||||||
legacy: { client },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} = ctx;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { has_all_requested: hasAllPrivileges, cluster } = await client.callAsCurrentUser(
|
const {
|
||||||
'transport.request',
|
body: { has_all_requested: hasAllPrivileges, cluster },
|
||||||
{
|
} = await client.asCurrentUser.security.hasPrivileges({
|
||||||
path: '/_security/user/_has_privileges',
|
body: {
|
||||||
method: 'POST',
|
cluster: ['manage_index_templates'],
|
||||||
body: {
|
},
|
||||||
cluster: ['manage_index_templates'],
|
});
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!hasAllPrivileges) {
|
if (!hasAllPrivileges) {
|
||||||
privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster);
|
privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
privilegesResult.hasAllPrivileges = hasAllPrivileges;
|
privilegesResult.hasAllPrivileges = hasAllPrivileges;
|
||||||
|
return response.ok({ body: privilegesResult });
|
||||||
return res.ok({ body: privilegesResult });
|
} catch (error) {
|
||||||
} catch (e) {
|
return handleEsError({ error, response });
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,10 @@ const paramsSchema = schema.object({
|
||||||
name: schema.string(),
|
name: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const registerUpdateRoute = ({ router, lib: { isEsError } }: RouteDependencies): void => {
|
export const registerUpdateRoute = ({
|
||||||
|
router,
|
||||||
|
lib: { handleEsError },
|
||||||
|
}: RouteDependencies): void => {
|
||||||
router.put(
|
router.put(
|
||||||
{
|
{
|
||||||
path: addBasePath('/component_templates/{name}'),
|
path: addBasePath('/component_templates/{name}'),
|
||||||
|
@ -24,34 +27,28 @@ export const registerUpdateRoute = ({ router, lib: { isEsError } }: RouteDepende
|
||||||
params: paramsSchema,
|
params: paramsSchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { name } = req.params;
|
const { name } = request.params;
|
||||||
const { template, version, _meta } = req.body;
|
const { template, version, _meta } = request.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Verify component exists; ES will throw 404 if not
|
// Verify component exists; ES will throw 404 if not
|
||||||
await callAsCurrentUser('dataManagement.getComponentTemplate', { name });
|
await client.asCurrentUser.cluster.getComponentTemplate({ name });
|
||||||
|
|
||||||
const response = await callAsCurrentUser('dataManagement.saveComponentTemplate', {
|
const { body: responseBody } = await client.asCurrentUser.cluster.putComponentTemplate({
|
||||||
name,
|
name,
|
||||||
body: {
|
body: {
|
||||||
|
// @ts-expect-error @elastic/elasticsearch Not assignable to type 'IndicesIndexState'
|
||||||
template,
|
template,
|
||||||
version,
|
version,
|
||||||
_meta,
|
_meta,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok({ body: response });
|
return response.ok({ body: responseBody });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isEsError(error)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: error.statusCode,
|
|
||||||
body: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,23 +9,22 @@ import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
import { wrapEsError } from '../../helpers';
|
|
||||||
|
|
||||||
const bodySchema = schema.object({
|
const bodySchema = schema.object({
|
||||||
dataStreams: schema.arrayOf(schema.string()),
|
dataStreams: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerDeleteRoute({ router }: RouteDependencies) {
|
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: addBasePath('/delete_data_streams'),
|
path: addBasePath('/delete_data_streams'),
|
||||||
validate: { body: bodySchema },
|
validate: { body: bodySchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { dataStreams } = req.body as TypeOf<typeof bodySchema>;
|
const { dataStreams } = request.body as TypeOf<typeof bodySchema>;
|
||||||
|
|
||||||
const response: { dataStreamsDeleted: string[]; errors: any[] } = {
|
const responseBody: { dataStreamsDeleted: string[]; errors: any[] } = {
|
||||||
dataStreamsDeleted: [],
|
dataStreamsDeleted: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
@ -33,21 +32,21 @@ export function registerDeleteRoute({ router }: RouteDependencies) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
dataStreams.map(async (name: string) => {
|
dataStreams.map(async (name: string) => {
|
||||||
try {
|
try {
|
||||||
await callAsCurrentUser('dataManagement.deleteDataStream', {
|
await client.asCurrentUser.indices.deleteDataStream({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.dataStreamsDeleted.push(name);
|
return responseBody.dataStreamsDeleted.push(name);
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
return response.errors.push({
|
return responseBody.errors.push({
|
||||||
name,
|
name,
|
||||||
error: wrapEsError(e),
|
error: handleEsError({ error, response }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.ok({ body: response });
|
return response.ok({ body: responseBody });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import { schema, TypeOf } from '@kbn/config-schema';
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
import { ElasticsearchClient } from 'kibana/server';
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
import { deserializeDataStream, deserializeDataStreamList } from '../../../../common/lib';
|
import { deserializeDataStream, deserializeDataStreamList } from '../../../../common/lib';
|
||||||
import { DataStreamFromEs } from '../../../../common/types';
|
import { DataStreamFromEs } from '../../../../common/types';
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
|
@ -68,30 +68,23 @@ const enhanceDataStreams = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDataStreams = (client: ElasticsearchClient, name = '*') => {
|
const getDataStreams = (client: IScopedClusterClient, name = '*') => {
|
||||||
// TODO update when elasticsearch client has update requestParams for 'indices.getDataStream'
|
return client.asCurrentUser.indices.getDataStream({
|
||||||
return client.transport.request({
|
name,
|
||||||
path: `/_data_stream/${encodeURIComponent(name)}`,
|
expand_wildcards: 'all',
|
||||||
method: 'GET',
|
|
||||||
querystring: {
|
|
||||||
expand_wildcards: 'all',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDataStreamsStats = (client: ElasticsearchClient, name = '*') => {
|
const getDataStreamsStats = (client: IScopedClusterClient, name = '*') => {
|
||||||
return client.transport.request({
|
return client.asCurrentUser.indices.dataStreamsStats({
|
||||||
path: `/_data_stream/${encodeURIComponent(name)}/_stats`,
|
name,
|
||||||
method: 'GET',
|
expand_wildcards: 'all',
|
||||||
querystring: {
|
human: true,
|
||||||
human: true,
|
|
||||||
expand_wildcards: 'all',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDataStreamsPrivileges = (client: ElasticsearchClient, names: string[]) => {
|
const getDataStreamsPrivileges = (client: IScopedClusterClient, names: string[]) => {
|
||||||
return client.security.hasPrivileges({
|
return client.asCurrentUser.security.hasPrivileges({
|
||||||
body: {
|
body: {
|
||||||
index: [
|
index: [
|
||||||
{
|
{
|
||||||
|
@ -109,15 +102,15 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }:
|
||||||
});
|
});
|
||||||
router.get(
|
router.get(
|
||||||
{ path: addBasePath('/data_streams'), validate: { query: querySchema } },
|
{ path: addBasePath('/data_streams'), validate: { query: querySchema } },
|
||||||
async (ctx, req, response) => {
|
async (context, request, response) => {
|
||||||
const { asCurrentUser } = ctx.core.elasticsearch.client;
|
const { client } = context.core.elasticsearch;
|
||||||
|
|
||||||
const includeStats = (req.query as TypeOf<typeof querySchema>).includeStats === 'true';
|
const includeStats = (request.query as TypeOf<typeof querySchema>).includeStats === 'true';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let {
|
const {
|
||||||
body: { data_streams: dataStreams },
|
body: { data_streams: dataStreams },
|
||||||
} = await getDataStreams(asCurrentUser);
|
} = await getDataStreams(client);
|
||||||
|
|
||||||
let dataStreamsStats;
|
let dataStreamsStats;
|
||||||
let dataStreamsPrivileges;
|
let dataStreamsPrivileges;
|
||||||
|
@ -125,24 +118,26 @@ export function registerGetAllRoute({ router, lib: { handleEsError }, config }:
|
||||||
if (includeStats) {
|
if (includeStats) {
|
||||||
({
|
({
|
||||||
body: { data_streams: dataStreamsStats },
|
body: { data_streams: dataStreamsStats },
|
||||||
} = await getDataStreamsStats(asCurrentUser));
|
} = await getDataStreamsStats(client));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isSecurityEnabled() && dataStreams.length > 0) {
|
if (config.isSecurityEnabled() && dataStreams.length > 0) {
|
||||||
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(
|
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(
|
||||||
asCurrentUser,
|
client,
|
||||||
dataStreams.map((dataStream: DataStreamFromEs) => dataStream.name)
|
dataStreams.map((dataStream) => dataStream.name)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
dataStreams = enhanceDataStreams({
|
const enhancedDataStreams = enhanceDataStreams({
|
||||||
|
// @ts-expect-error @elastic/elasticsearch DataStreamFromEs incompatible with IndicesGetDataStreamIndicesGetDataStreamItem
|
||||||
dataStreams,
|
dataStreams,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch StatsFromEs incompatible with IndicesDataStreamsStatsDataStreamsStatsItem
|
||||||
dataStreamsStats,
|
dataStreamsStats,
|
||||||
// @ts-expect-error PrivilegesFromEs incompatible with ApplicationsPrivileges
|
// @ts-expect-error @elastic/elasticsearch PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||||
dataStreamsPrivileges,
|
dataStreamsPrivileges,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.ok({ body: deserializeDataStreamList(dataStreams) });
|
return response.ok({ body: deserializeDataStreamList(enhancedDataStreams) });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleEsError({ error, response });
|
return handleEsError({ error, response });
|
||||||
}
|
}
|
||||||
|
@ -159,9 +154,9 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }:
|
||||||
path: addBasePath('/data_streams/{name}'),
|
path: addBasePath('/data_streams/{name}'),
|
||||||
validate: { params: paramsSchema },
|
validate: { params: paramsSchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, response) => {
|
async (context, request, response) => {
|
||||||
const { name } = req.params as TypeOf<typeof paramsSchema>;
|
const { name } = request.params as TypeOf<typeof paramsSchema>;
|
||||||
const { asCurrentUser } = ctx.core.elasticsearch.client;
|
const { client } = context.core.elasticsearch;
|
||||||
try {
|
try {
|
||||||
const [
|
const [
|
||||||
{
|
{
|
||||||
|
@ -170,23 +165,22 @@ export function registerGetOneRoute({ router, lib: { handleEsError }, config }:
|
||||||
{
|
{
|
||||||
body: { data_streams: dataStreamsStats },
|
body: { data_streams: dataStreamsStats },
|
||||||
},
|
},
|
||||||
] = await Promise.all([
|
] = await Promise.all([getDataStreams(client, name), getDataStreamsStats(client, name)]);
|
||||||
getDataStreams(asCurrentUser, name),
|
|
||||||
getDataStreamsStats(asCurrentUser, name),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (dataStreams[0]) {
|
if (dataStreams[0]) {
|
||||||
let dataStreamsPrivileges;
|
let dataStreamsPrivileges;
|
||||||
if (config.isSecurityEnabled()) {
|
if (config.isSecurityEnabled()) {
|
||||||
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(asCurrentUser, [
|
({ body: dataStreamsPrivileges } = await getDataStreamsPrivileges(client, [
|
||||||
dataStreams[0].name,
|
dataStreams[0].name,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const enhancedDataStreams = enhanceDataStreams({
|
const enhancedDataStreams = enhanceDataStreams({
|
||||||
|
// @ts-expect-error @elastic/elasticsearch DataStreamFromEs incompatible with IndicesGetDataStreamIndicesGetDataStreamItem
|
||||||
dataStreams,
|
dataStreams,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch StatsFromEs incompatible with IndicesDataStreamsStatsDataStreamsStatsItem
|
||||||
dataStreamsStats,
|
dataStreamsStats,
|
||||||
// @ts-expect-error PrivilegesFromEs incompatible with ApplicationsPrivileges
|
// @ts-expect-error @elastic/elasticsearch PrivilegesFromEs incompatible with ApplicationsPrivileges
|
||||||
dataStreamsPrivileges,
|
dataStreamsPrivileges,
|
||||||
});
|
});
|
||||||
const body = deserializeDataStream(enhancedDataStreams[0]);
|
const body = deserializeDataStream(enhancedDataStreams[0]);
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerClearCacheRoute({ router, lib }: RouteDependencies) {
|
export function registerClearCacheRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/clear_cache'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/clear_cache'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const payload = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = payload;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.clearCache', params);
|
await client.asCurrentUser.indices.clearCache(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerCloseRoute({ router, lib }: RouteDependencies) {
|
export function registerCloseRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/close'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/close'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const payload = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = payload;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.close', params);
|
await client.asCurrentUser.indices.close(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerDeleteRoute({ router, lib }: RouteDependencies) {
|
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/delete'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/delete'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const body = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = body;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.delete', params);
|
await client.asCurrentUser.indices.delete(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerFlushRoute({ router, lib }: RouteDependencies) {
|
export function registerFlushRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/flush'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/flush'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const body = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = body;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.flush', params);
|
await client.asCurrentUser.indices.flush(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -15,7 +15,7 @@ const bodySchema = schema.object({
|
||||||
maxNumSegments: schema.maybe(schema.number()),
|
maxNumSegments: schema.maybe(schema.number()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
export function registerForcemergeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: addBasePath('/indices/forcemerge'),
|
path: addBasePath('/indices/forcemerge'),
|
||||||
|
@ -23,10 +23,11 @@ export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
||||||
body: bodySchema,
|
body: bodySchema,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { maxNumSegments, indices = [] } = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { maxNumSegments, indices = [] } = request.body as typeof bodySchema.type;
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,17 +36,10 @@ export function registerForcemergeRoute({ router, lib }: RouteDependencies) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.forcemerge', params);
|
await client.asCurrentUser.indices.forcemerge(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,33 +14,20 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerFreezeRoute({ router, lib }: RouteDependencies) {
|
export function registerFreezeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/freeze'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/freeze'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const body = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = body;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
|
||||||
path: `/${encodeURIComponent(indices.join(','))}/_freeze`,
|
|
||||||
method: 'POST',
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
await client.asCurrentUser.indices.freeze({
|
||||||
'transport.request',
|
index: indices.join(','),
|
||||||
params
|
});
|
||||||
);
|
return response.ok();
|
||||||
return res.ok();
|
} catch (error) {
|
||||||
} catch (e) {
|
return handleEsError({ error, response });
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,23 +9,21 @@ import { fetchIndices } from '../../../lib/fetch_indices';
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
|
|
||||||
export function registerListRoute({ router, indexDataEnricher, lib }: RouteDependencies) {
|
export function registerListRoute({
|
||||||
router.get({ path: addBasePath('/indices'), validate: false }, async (ctx, req, res) => {
|
router,
|
||||||
try {
|
indexDataEnricher,
|
||||||
const indices = await fetchIndices(
|
lib: { handleEsError },
|
||||||
ctx.core.elasticsearch.legacy.client.callAsCurrentUser,
|
}: RouteDependencies) {
|
||||||
indexDataEnricher
|
router.get(
|
||||||
);
|
{ path: addBasePath('/indices'), validate: false },
|
||||||
return res.ok({ body: indices });
|
async (context, request, response) => {
|
||||||
} catch (e) {
|
const { client } = context.core.elasticsearch;
|
||||||
if (lib.isEsError(e)) {
|
try {
|
||||||
return res.customError({
|
const indices = await fetchIndices(client, indexDataEnricher);
|
||||||
statusCode: e.statusCode,
|
return response.ok({ body: indices });
|
||||||
body: e,
|
} catch (error) {
|
||||||
});
|
return handleEsError({ error, response });
|
||||||
}
|
}
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerOpenRoute({ router, lib }: RouteDependencies) {
|
export function registerOpenRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/open'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/open'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const body = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = body;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.open', params);
|
await client.asCurrentUser.indices.open(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,31 +14,24 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerRefreshRoute({ router, lib }: RouteDependencies) {
|
export function registerRefreshRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/refresh'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/refresh'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const body = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const { indices = [] } = body;
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
index: indices,
|
index: indices,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('indices.refresh', params);
|
await client.asCurrentUser.indices.refresh(params);
|
||||||
return res.ok();
|
return response.ok();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,28 +17,22 @@ const bodySchema = schema.maybe(
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
export function registerReloadRoute({ router, indexDataEnricher, lib }: RouteDependencies) {
|
export function registerReloadRoute({
|
||||||
|
router,
|
||||||
|
indexDataEnricher,
|
||||||
|
lib: { handleEsError },
|
||||||
|
}: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/reload'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/reload'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indexNames = [] } = (req.body as typeof bodySchema.type) ?? {};
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { indexNames = [] } = (request.body as typeof bodySchema.type) ?? {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const indices = await fetchIndices(
|
const indices = await fetchIndices(client, indexDataEnricher, indexNames);
|
||||||
ctx.core.elasticsearch.legacy.client.callAsCurrentUser,
|
return response.ok({ body: indices });
|
||||||
indexDataEnricher,
|
} catch (error) {
|
||||||
indexNames
|
return handleEsError({ error, response });
|
||||||
);
|
|
||||||
return res.ok({ body: indices });
|
|
||||||
} catch (e) {
|
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,28 +14,20 @@ const bodySchema = schema.object({
|
||||||
indices: schema.arrayOf(schema.string()),
|
indices: schema.arrayOf(schema.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerUnfreezeRoute({ router, lib }: RouteDependencies) {
|
export function registerUnfreezeRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/indices/unfreeze'), validate: { body: bodySchema } },
|
{ path: addBasePath('/indices/unfreeze'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indices = [] } = req.body as typeof bodySchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
const params = {
|
const { indices = [] } = request.body as typeof bodySchema.type;
|
||||||
path: `/${encodeURIComponent(indices.join(','))}/_unfreeze`,
|
|
||||||
method: 'POST',
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.core.elasticsearch.legacy.client.callAsCurrentUser('transport.request', params);
|
await client.asCurrentUser.indices.unfreeze({
|
||||||
return res.ok();
|
index: indices.join(','),
|
||||||
} catch (e) {
|
});
|
||||||
if (lib.isEsError(e)) {
|
return response.ok();
|
||||||
return res.customError({
|
} catch (error) {
|
||||||
statusCode: e.statusCode,
|
return handleEsError({ error, response });
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,32 +21,23 @@ function formatHit(hit: { [key: string]: { mappings: any } }, indexName: string)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerMappingRoute({ router, lib }: RouteDependencies) {
|
export function registerMappingRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.get(
|
router.get(
|
||||||
{ path: addBasePath('/mapping/{indexName}'), validate: { params: paramsSchema } },
|
{ path: addBasePath('/mapping/{indexName}'), validate: { params: paramsSchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indexName } = req.params as typeof paramsSchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { indexName } = request.params as typeof paramsSchema.type;
|
||||||
const params = {
|
const params = {
|
||||||
expand_wildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
index: indexName,
|
index: indexName,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
const { body: hit } = await client.asCurrentUser.indices.getMapping(params);
|
||||||
'indices.getMapping',
|
const responseBody = formatHit(hit, indexName);
|
||||||
params
|
return response.ok({ body: responseBody });
|
||||||
);
|
} catch (error) {
|
||||||
const response = formatHit(hit, indexName);
|
return handleEsError({ error, response });
|
||||||
return res.ok({ body: response });
|
|
||||||
} catch (e) {
|
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,34 +21,25 @@ function formatHit(hit: { [key: string]: {} }) {
|
||||||
return hit[key];
|
return hit[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerLoadRoute({ router, lib }: RouteDependencies) {
|
export function registerLoadRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.get(
|
router.get(
|
||||||
{ path: addBasePath('/settings/{indexName}'), validate: { params: paramsSchema } },
|
{ path: addBasePath('/settings/{indexName}'), validate: { params: paramsSchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indexName } = req.params as typeof paramsSchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { indexName } = request.params as typeof paramsSchema.type;
|
||||||
const params = {
|
const params = {
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
flatSettings: false,
|
flat_settings: false,
|
||||||
local: false,
|
local: false,
|
||||||
includeDefaults: true,
|
include_defaults: true,
|
||||||
index: indexName,
|
index: indexName,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
const { body: hit } = await client.asCurrentUser.indices.getSettings(params);
|
||||||
'indices.getSettings',
|
return response.ok({ body: formatHit(hit) });
|
||||||
params
|
} catch (error) {
|
||||||
);
|
return handleEsError({ error, response });
|
||||||
return res.ok({ body: formatHit(hit) });
|
|
||||||
} catch (e) {
|
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,37 +16,28 @@ const paramsSchema = schema.object({
|
||||||
indexName: schema.string(),
|
indexName: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerUpdateRoute({ router, lib }: RouteDependencies) {
|
export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.put(
|
router.put(
|
||||||
{
|
{
|
||||||
path: addBasePath('/settings/{indexName}'),
|
path: addBasePath('/settings/{indexName}'),
|
||||||
validate: { body: bodySchema, params: paramsSchema },
|
validate: { body: bodySchema, params: paramsSchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indexName } = req.params as typeof paramsSchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { indexName } = request.params as typeof paramsSchema.type;
|
||||||
const params = {
|
const params = {
|
||||||
ignoreUnavailable: true,
|
ignore_unavailable: true,
|
||||||
allowNoIndices: false,
|
allow_no_indices: false,
|
||||||
expandWildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
index: indexName,
|
index: indexName,
|
||||||
body: req.body,
|
body: request.body,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
const { body: responseBody } = await client.asCurrentUser.indices.putSettings(params);
|
||||||
'indices.putSettings',
|
return response.ok({ body: responseBody });
|
||||||
params
|
} catch (error) {
|
||||||
);
|
return handleEsError({ error, response });
|
||||||
return res.ok({ body: response });
|
|
||||||
} catch (e) {
|
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { schema } from '@kbn/config-schema';
|
import { schema } from '@kbn/config-schema';
|
||||||
|
import type { estypes } from '@elastic/elasticsearch';
|
||||||
|
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
|
@ -14,40 +15,37 @@ const paramsSchema = schema.object({
|
||||||
indexName: schema.string(),
|
indexName: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
function formatHit(hit: { _shards: any; indices: { [key: string]: any } }, indexName: string) {
|
interface Hit {
|
||||||
|
_shards: unknown;
|
||||||
|
indices?: Record<string, estypes.IndicesStatsIndicesStats>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatHit(hit: Hit, indexName: string) {
|
||||||
const { _shards, indices } = hit;
|
const { _shards, indices } = hit;
|
||||||
const stats = indices[indexName];
|
const stats = indices![indexName];
|
||||||
return {
|
return {
|
||||||
_shards,
|
_shards,
|
||||||
stats,
|
stats,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerStatsRoute({ router, lib }: RouteDependencies) {
|
export function registerStatsRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.get(
|
router.get(
|
||||||
{ path: addBasePath('/stats/{indexName}'), validate: { params: paramsSchema } },
|
{ path: addBasePath('/stats/{indexName}'), validate: { params: paramsSchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { indexName } = req.params as typeof paramsSchema.type;
|
const { client } = context.core.elasticsearch;
|
||||||
|
const { indexName } = request.params as typeof paramsSchema.type;
|
||||||
const params = {
|
const params = {
|
||||||
expand_wildcards: 'none',
|
expand_wildcards: 'none',
|
||||||
index: indexName,
|
index: indexName,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const hit = await ctx.core.elasticsearch.legacy.client.callAsCurrentUser(
|
const { body: hit } = await client.asCurrentUser.indices.stats(params);
|
||||||
'indices.stats',
|
|
||||||
params
|
return response.ok({ body: formatHit(hit, indexName) });
|
||||||
);
|
} catch (error) {
|
||||||
return res.ok({ body: formatHit(hit, indexName) });
|
return handleEsError({ error, response });
|
||||||
} catch (e) {
|
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,32 +5,33 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
import { serializeTemplate, serializeLegacyTemplate } from '../../../../common/lib';
|
import { serializeTemplate, serializeLegacyTemplate } from '../../../../common/lib';
|
||||||
import { TemplateDeserialized, LegacyTemplateSerialized } from '../../../../common';
|
import { TemplateDeserialized, LegacyTemplateSerialized } from '../../../../common';
|
||||||
import { CallAsCurrentUser } from '../../../types';
|
|
||||||
|
|
||||||
export const doesTemplateExist = async ({
|
export const doesTemplateExist = async ({
|
||||||
name,
|
name,
|
||||||
callAsCurrentUser,
|
client,
|
||||||
isLegacy,
|
isLegacy,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
callAsCurrentUser: CallAsCurrentUser;
|
client: IScopedClusterClient;
|
||||||
isLegacy?: boolean;
|
isLegacy?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
if (isLegacy) {
|
if (isLegacy) {
|
||||||
return await callAsCurrentUser('indices.existsTemplate', { name });
|
return await client.asCurrentUser.indices.existsTemplate({ name });
|
||||||
}
|
}
|
||||||
return await callAsCurrentUser('dataManagement.existsTemplate', { name });
|
|
||||||
|
return await client.asCurrentUser.indices.existsIndexTemplate({ name });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const saveTemplate = async ({
|
export const saveTemplate = async ({
|
||||||
template,
|
template,
|
||||||
callAsCurrentUser,
|
client,
|
||||||
isLegacy,
|
isLegacy,
|
||||||
}: {
|
}: {
|
||||||
template: TemplateDeserialized;
|
template: TemplateDeserialized;
|
||||||
callAsCurrentUser: CallAsCurrentUser;
|
client: IScopedClusterClient;
|
||||||
isLegacy?: boolean;
|
isLegacy?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const serializedTemplate = isLegacy
|
const serializedTemplate = isLegacy
|
||||||
|
@ -48,8 +49,9 @@ export const saveTemplate = async ({
|
||||||
aliases,
|
aliases,
|
||||||
} = serializedTemplate as LegacyTemplateSerialized;
|
} = serializedTemplate as LegacyTemplateSerialized;
|
||||||
|
|
||||||
return await callAsCurrentUser('indices.putTemplate', {
|
return await client.asCurrentUser.indices.putTemplate({
|
||||||
name: template.name,
|
name: template.name,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch not assignable to parameter of type 'IndicesPutTemplateRequest'
|
||||||
order,
|
order,
|
||||||
body: {
|
body: {
|
||||||
index_patterns,
|
index_patterns,
|
||||||
|
@ -61,8 +63,9 @@ export const saveTemplate = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return await callAsCurrentUser('dataManagement.saveComposableIndexTemplate', {
|
return await client.asCurrentUser.indices.putIndexTemplate({
|
||||||
name: template.name,
|
name: template.name,
|
||||||
|
// @ts-expect-error @elastic/elasticsearch Type 'LegacyTemplateSerialized | TemplateSerialized' is not assignable
|
||||||
body: serializedTemplate,
|
body: serializedTemplate,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,54 +15,44 @@ import { saveTemplate, doesTemplateExist } from './lib';
|
||||||
|
|
||||||
const bodySchema = templateSchema;
|
const bodySchema = templateSchema;
|
||||||
|
|
||||||
export function registerCreateRoute({ router, lib }: RouteDependencies) {
|
export function registerCreateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{ path: addBasePath('/index_templates'), validate: { body: bodySchema } },
|
{ path: addBasePath('/index_templates'), validate: { body: bodySchema } },
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const template = req.body as TemplateDeserialized;
|
const template = request.body as TemplateDeserialized;
|
||||||
const {
|
|
||||||
_kbnMeta: { isLegacy },
|
|
||||||
} = template;
|
|
||||||
|
|
||||||
// Check that template with the same name doesn't already exist
|
|
||||||
const templateExists = await doesTemplateExist({
|
|
||||||
name: template.name,
|
|
||||||
callAsCurrentUser,
|
|
||||||
isLegacy,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (templateExists) {
|
|
||||||
return res.conflict({
|
|
||||||
body: new Error(
|
|
||||||
i18n.translate('xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage', {
|
|
||||||
defaultMessage: "There is already a template with name '{name}'.",
|
|
||||||
values: {
|
|
||||||
name: template.name,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Otherwise create new index template
|
const {
|
||||||
const response = await saveTemplate({ template, callAsCurrentUser, isLegacy });
|
_kbnMeta: { isLegacy },
|
||||||
|
} = template;
|
||||||
|
|
||||||
return res.ok({ body: response });
|
// Check that template with the same name doesn't already exist
|
||||||
} catch (e) {
|
const { body: templateExists } = await doesTemplateExist({
|
||||||
if (lib.isEsError(e)) {
|
name: template.name,
|
||||||
const error = lib.parseEsError(e.response);
|
client,
|
||||||
return res.customError({
|
isLegacy,
|
||||||
statusCode: e.statusCode,
|
});
|
||||||
body: {
|
|
||||||
message: error.message,
|
if (templateExists) {
|
||||||
attributes: error,
|
return response.conflict({
|
||||||
},
|
body: new Error(
|
||||||
|
i18n.translate('xpack.idxMgmt.createRoute.duplicateTemplateIdErrorMessage', {
|
||||||
|
defaultMessage: "There is already a template with name '{name}'.",
|
||||||
|
values: {
|
||||||
|
name: template.name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Case: default
|
|
||||||
throw e;
|
// Otherwise create new index template
|
||||||
|
const { body: responseBody } = await saveTemplate({ template, client, isLegacy });
|
||||||
|
|
||||||
|
return response.ok({ body: responseBody });
|
||||||
|
} catch (error) {
|
||||||
|
return handleEsError({ error, response });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
import { wrapEsError } from '../../helpers';
|
|
||||||
|
|
||||||
import { TemplateDeserialized } from '../../../../common';
|
import { TemplateDeserialized } from '../../../../common';
|
||||||
|
|
||||||
|
@ -22,16 +21,19 @@ const bodySchema = schema.object({
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerDeleteRoute({ router }: RouteDependencies) {
|
export function registerDeleteRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: addBasePath('/delete_index_templates'),
|
path: addBasePath('/delete_index_templates'),
|
||||||
validate: { body: bodySchema },
|
validate: { body: bodySchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { templates } = req.body as TypeOf<typeof bodySchema>;
|
const { templates } = request.body as TypeOf<typeof bodySchema>;
|
||||||
const response: { templatesDeleted: Array<TemplateDeserialized['name']>; errors: any[] } = {
|
const responseBody: {
|
||||||
|
templatesDeleted: Array<TemplateDeserialized['name']>;
|
||||||
|
errors: any[];
|
||||||
|
} = {
|
||||||
templatesDeleted: [],
|
templatesDeleted: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
};
|
};
|
||||||
|
@ -40,26 +42,26 @@ export function registerDeleteRoute({ router }: RouteDependencies) {
|
||||||
templates.map(async ({ name, isLegacy }) => {
|
templates.map(async ({ name, isLegacy }) => {
|
||||||
try {
|
try {
|
||||||
if (isLegacy) {
|
if (isLegacy) {
|
||||||
await callAsCurrentUser('indices.deleteTemplate', {
|
await client.asCurrentUser.indices.deleteTemplate({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await callAsCurrentUser('dataManagement.deleteComposableIndexTemplate', {
|
await client.asCurrentUser.indices.deleteIndexTemplate({
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.templatesDeleted.push(name);
|
return responseBody.templatesDeleted.push(name);
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
return response.errors.push({
|
return responseBody.errors.push({
|
||||||
name,
|
name,
|
||||||
error: wrapEsError(e),
|
error: handleEsError({ error, response }),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.ok({ body: response });
|
return response.ok({ body: responseBody });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,41 +17,37 @@ import { getCloudManagedTemplatePrefix } from '../../../lib/get_managed_template
|
||||||
import { RouteDependencies } from '../../../types';
|
import { RouteDependencies } from '../../../types';
|
||||||
import { addBasePath } from '../index';
|
import { addBasePath } from '../index';
|
||||||
|
|
||||||
export function registerGetAllRoute({ router, lib: { isEsError } }: RouteDependencies) {
|
export function registerGetAllRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.get({ path: addBasePath('/index_templates'), validate: false }, async (ctx, req, res) => {
|
router.get(
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
{ path: addBasePath('/index_templates'), validate: false },
|
||||||
|
async (context, request, response) => {
|
||||||
|
const { client } = context.core.elasticsearch;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(callAsCurrentUser);
|
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(client);
|
||||||
|
|
||||||
const legacyTemplatesEs = await callAsCurrentUser('indices.getTemplate');
|
const { body: legacyTemplatesEs } = await client.asCurrentUser.indices.getTemplate();
|
||||||
const { index_templates: templatesEs } = await callAsCurrentUser(
|
const {
|
||||||
'dataManagement.getComposableIndexTemplates'
|
body: { index_templates: templatesEs },
|
||||||
);
|
} = await client.asCurrentUser.indices.getIndexTemplate();
|
||||||
|
|
||||||
const legacyTemplates = deserializeLegacyTemplateList(
|
const legacyTemplates = deserializeLegacyTemplateList(
|
||||||
legacyTemplatesEs,
|
legacyTemplatesEs,
|
||||||
cloudManagedTemplatePrefix
|
cloudManagedTemplatePrefix
|
||||||
);
|
);
|
||||||
const templates = deserializeTemplateList(templatesEs, cloudManagedTemplatePrefix);
|
const templates = deserializeTemplateList(templatesEs, cloudManagedTemplatePrefix);
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
templates,
|
templates,
|
||||||
legacyTemplates,
|
legacyTemplates,
|
||||||
};
|
};
|
||||||
|
|
||||||
return res.ok({ body });
|
return response.ok({ body });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isEsError(error)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: error.statusCode,
|
|
||||||
body: error,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Case: default
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsSchema = schema.object({
|
const paramsSchema = schema.object({
|
||||||
|
@ -63,26 +59,27 @@ const querySchema = schema.object({
|
||||||
legacy: schema.maybe(schema.oneOf([schema.literal('true'), schema.literal('false')])),
|
legacy: schema.maybe(schema.oneOf([schema.literal('true'), schema.literal('false')])),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
export function registerGetOneRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.get(
|
router.get(
|
||||||
{
|
{
|
||||||
path: addBasePath('/index_templates/{name}'),
|
path: addBasePath('/index_templates/{name}'),
|
||||||
validate: { params: paramsSchema, query: querySchema },
|
validate: { params: paramsSchema, query: querySchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { name } = req.params as TypeOf<typeof paramsSchema>;
|
const { client } = context.core.elasticsearch;
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { name } = request.params as TypeOf<typeof paramsSchema>;
|
||||||
|
const isLegacy = (request.query as TypeOf<typeof querySchema>).legacy === 'true';
|
||||||
const isLegacy = (req.query as TypeOf<typeof querySchema>).legacy === 'true';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(callAsCurrentUser);
|
const cloudManagedTemplatePrefix = await getCloudManagedTemplatePrefix(client);
|
||||||
|
|
||||||
if (isLegacy) {
|
if (isLegacy) {
|
||||||
const indexTemplateByName = await callAsCurrentUser('indices.getTemplate', { name });
|
const { body: indexTemplateByName } = await client.asCurrentUser.indices.getTemplate({
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
|
||||||
if (indexTemplateByName[name]) {
|
if (indexTemplateByName[name]) {
|
||||||
return res.ok({
|
return response.ok({
|
||||||
body: deserializeLegacyTemplate(
|
body: deserializeLegacyTemplate(
|
||||||
{ ...indexTemplateByName[name], name },
|
{ ...indexTemplateByName[name], name },
|
||||||
cloudManagedTemplatePrefix
|
cloudManagedTemplatePrefix
|
||||||
|
@ -91,11 +88,11 @@ export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
index_templates: indexTemplates,
|
body: { index_templates: indexTemplates },
|
||||||
} = await callAsCurrentUser('dataManagement.getComposableIndexTemplate', { name });
|
} = await client.asCurrentUser.indices.getIndexTemplate({ name });
|
||||||
|
|
||||||
if (indexTemplates.length > 0) {
|
if (indexTemplates.length > 0) {
|
||||||
return res.ok({
|
return response.ok({
|
||||||
body: deserializeTemplate(
|
body: deserializeTemplate(
|
||||||
{ ...indexTemplates[0].index_template, name },
|
{ ...indexTemplates[0].index_template, name },
|
||||||
cloudManagedTemplatePrefix
|
cloudManagedTemplatePrefix
|
||||||
|
@ -104,16 +101,9 @@ export function registerGetOneRoute({ router, lib }: RouteDependencies) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.notFound();
|
return response.notFound();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,35 +12,30 @@ import { addBasePath } from '../index';
|
||||||
|
|
||||||
const bodySchema = schema.object({}, { unknowns: 'allow' });
|
const bodySchema = schema.object({}, { unknowns: 'allow' });
|
||||||
|
|
||||||
export function registerSimulateRoute({ router, lib }: RouteDependencies) {
|
export function registerSimulateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.post(
|
router.post(
|
||||||
{
|
{
|
||||||
path: addBasePath('/index_templates/simulate'),
|
path: addBasePath('/index_templates/simulate'),
|
||||||
validate: { body: bodySchema },
|
validate: { body: bodySchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const template = req.body as TypeOf<typeof bodySchema>;
|
const template = request.body as TypeOf<typeof bodySchema>;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const templatePreview = await callAsCurrentUser('dataManagement.simulateTemplate', {
|
const { body: templatePreview } = await client.asCurrentUser.indices.simulateTemplate({
|
||||||
body: template,
|
body: {
|
||||||
|
...template,
|
||||||
|
// Until ES fixes a bug on their side we need to send a fake index pattern
|
||||||
|
// that won't match any indices.
|
||||||
|
// Issue: https://github.com/elastic/elasticsearch/issues/59152
|
||||||
|
index_patterns: ['a_fake_index_pattern_that_wont_match_any_indices'],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.ok({ body: templatePreview });
|
return response.ok({ body: templatePreview });
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
if (lib.isEsError(e)) {
|
return handleEsError({ error, response });
|
||||||
const error = lib.parseEsError(e.response);
|
|
||||||
return res.customError({
|
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: {
|
|
||||||
message: error.message,
|
|
||||||
attributes: error,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Case: default
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,45 +18,35 @@ const paramsSchema = schema.object({
|
||||||
name: schema.string(),
|
name: schema.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function registerUpdateRoute({ router, lib }: RouteDependencies) {
|
export function registerUpdateRoute({ router, lib: { handleEsError } }: RouteDependencies) {
|
||||||
router.put(
|
router.put(
|
||||||
{
|
{
|
||||||
path: addBasePath('/index_templates/{name}'),
|
path: addBasePath('/index_templates/{name}'),
|
||||||
validate: { body: bodySchema, params: paramsSchema },
|
validate: { body: bodySchema, params: paramsSchema },
|
||||||
},
|
},
|
||||||
async (ctx, req, res) => {
|
async (context, request, response) => {
|
||||||
const { callAsCurrentUser } = ctx.dataManagement!.client;
|
const { client } = context.core.elasticsearch;
|
||||||
const { name } = req.params as typeof paramsSchema.type;
|
const { name } = request.params as typeof paramsSchema.type;
|
||||||
const template = req.body as TemplateDeserialized;
|
const template = request.body as TemplateDeserialized;
|
||||||
const {
|
|
||||||
_kbnMeta: { isLegacy },
|
|
||||||
} = template;
|
|
||||||
|
|
||||||
// Verify the template exists (ES will throw 404 if not)
|
|
||||||
const doesExist = await doesTemplateExist({ name, callAsCurrentUser, isLegacy });
|
|
||||||
|
|
||||||
if (!doesExist) {
|
|
||||||
return res.notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Next, update index template
|
const {
|
||||||
const response = await saveTemplate({ template, callAsCurrentUser, isLegacy });
|
_kbnMeta: { isLegacy },
|
||||||
|
} = template;
|
||||||
|
|
||||||
return res.ok({ body: response });
|
// Verify the template exists (ES will throw 404 if not)
|
||||||
} catch (e) {
|
const { body: templateExists } = await doesTemplateExist({ name, client, isLegacy });
|
||||||
if (lib.isEsError(e)) {
|
|
||||||
const error = lib.parseEsError(e.response);
|
if (!templateExists) {
|
||||||
return res.customError({
|
return response.notFound();
|
||||||
statusCode: e.statusCode,
|
|
||||||
body: {
|
|
||||||
message: error.message,
|
|
||||||
attributes: error,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Case: default
|
|
||||||
throw e;
|
// Next, update index template
|
||||||
|
const { body: responseBody } = await saveTemplate({ template, client, isLegacy });
|
||||||
|
|
||||||
|
return response.ok({ body: responseBody });
|
||||||
|
} catch (error) {
|
||||||
|
return handleEsError({ error, response });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
||||||
* or more contributor license agreements. Licensed under the Elastic License
|
|
||||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const extractCausedByChain = (causedBy: any = {}, accumulator: any[] = []): any => {
|
|
||||||
const { reason, caused_by } = causedBy; // eslint-disable-line @typescript-eslint/naming-convention
|
|
||||||
|
|
||||||
if (reason) {
|
|
||||||
accumulator.push(reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caused_by) {
|
|
||||||
return extractCausedByChain(caused_by, accumulator);
|
|
||||||
}
|
|
||||||
|
|
||||||
return accumulator;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps an error thrown by the ES JS client into a Boom error response and returns it
|
|
||||||
*
|
|
||||||
* @param err Object Error thrown by ES JS client
|
|
||||||
* @param statusCodeToMessageMap Object Optional map of HTTP status codes => error messages
|
|
||||||
* @return Object Boom error response
|
|
||||||
*/
|
|
||||||
export const wrapEsError = (err: any, statusCodeToMessageMap: any = {}) => {
|
|
||||||
const { statusCode, response } = err;
|
|
||||||
|
|
||||||
const {
|
|
||||||
error: {
|
|
||||||
root_cause = [], // eslint-disable-line @typescript-eslint/naming-convention
|
|
||||||
caused_by = {}, // eslint-disable-line @typescript-eslint/naming-convention
|
|
||||||
} = {},
|
|
||||||
} = JSON.parse(response);
|
|
||||||
|
|
||||||
// If no custom message if specified for the error's status code, just
|
|
||||||
// wrap the error as a Boom error response, include the additional information from ES, and return it
|
|
||||||
if (!statusCodeToMessageMap[statusCode]) {
|
|
||||||
// const boomError = Boom.boomify(err, { statusCode });
|
|
||||||
const error: any = { statusCode };
|
|
||||||
|
|
||||||
// The caused_by chain has the most information so use that if it's available. If not then
|
|
||||||
// settle for the root_cause.
|
|
||||||
const causedByChain = extractCausedByChain(caused_by);
|
|
||||||
const defaultCause = root_cause.length ? extractCausedByChain(root_cause[0]) : undefined;
|
|
||||||
|
|
||||||
error.cause = causedByChain.length ? causedByChain : defaultCause;
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, use the custom message to create a Boom error response and
|
|
||||||
// return it
|
|
||||||
const message = statusCodeToMessageMap[statusCode];
|
|
||||||
return { message, statusCode };
|
|
||||||
};
|
|
|
@ -5,10 +5,10 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CallAsCurrentUser } from '../types';
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
import { Index } from '../index';
|
import { Index } from '../index';
|
||||||
|
|
||||||
export type Enricher = (indices: Index[], callAsCurrentUser: CallAsCurrentUser) => Promise<Index[]>;
|
export type Enricher = (indices: Index[], client: IScopedClusterClient) => Promise<Index[]>;
|
||||||
|
|
||||||
export class IndexDataEnricher {
|
export class IndexDataEnricher {
|
||||||
private readonly _enrichers: Enricher[] = [];
|
private readonly _enrichers: Enricher[] = [];
|
||||||
|
@ -19,14 +19,14 @@ export class IndexDataEnricher {
|
||||||
|
|
||||||
public enrichIndices = async (
|
public enrichIndices = async (
|
||||||
indices: Index[],
|
indices: Index[],
|
||||||
callAsCurrentUser: CallAsCurrentUser
|
client: IScopedClusterClient
|
||||||
): Promise<Index[]> => {
|
): Promise<Index[]> => {
|
||||||
let enrichedIndices = indices;
|
let enrichedIndices = indices;
|
||||||
|
|
||||||
for (let i = 0; i < this.enrichers.length; i++) {
|
for (let i = 0; i < this.enrichers.length; i++) {
|
||||||
const dataEnricher = this.enrichers[i];
|
const dataEnricher = this.enrichers[i];
|
||||||
try {
|
try {
|
||||||
const dataEnricherResponse = await dataEnricher(enrichedIndices, callAsCurrentUser);
|
const dataEnricherResponse = await dataEnricher(enrichedIndices, client);
|
||||||
enrichedIndices = dataEnricherResponse;
|
enrichedIndices = dataEnricherResponse;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// silently swallow enricher response errors
|
// silently swallow enricher response errors
|
||||||
|
|
|
@ -5,8 +5,4 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
export { handleEsError } from '../../../../src/plugins/es_ui_shared/server';
|
||||||
isEsError,
|
|
||||||
parseEsError,
|
|
||||||
handleEsError,
|
|
||||||
} from '../../../../src/plugins/es_ui_shared/server';
|
|
||||||
|
|
|
@ -5,17 +5,13 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import { IRouter } from 'src/core/server';
|
||||||
LegacyScopedClusterClient,
|
|
||||||
ILegacyScopedClusterClient,
|
|
||||||
IRouter,
|
|
||||||
RequestHandlerContext,
|
|
||||||
} from 'src/core/server';
|
|
||||||
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
|
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
|
||||||
import { LicensingPluginSetup } from '../../licensing/server';
|
import { LicensingPluginSetup } from '../../licensing/server';
|
||||||
import { SecurityPluginSetup } from '../../security/server';
|
import { SecurityPluginSetup } from '../../security/server';
|
||||||
import { IndexDataEnricher } from './services';
|
import { IndexDataEnricher } from './services';
|
||||||
import { isEsError, parseEsError, handleEsError } from './shared_imports';
|
import { handleEsError } from './shared_imports';
|
||||||
|
|
||||||
export interface Dependencies {
|
export interface Dependencies {
|
||||||
security: SecurityPluginSetup;
|
security: SecurityPluginSetup;
|
||||||
|
@ -24,39 +20,12 @@ export interface Dependencies {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RouteDependencies {
|
export interface RouteDependencies {
|
||||||
router: IndexManagementRouter;
|
router: IRouter;
|
||||||
config: {
|
config: {
|
||||||
isSecurityEnabled: () => boolean;
|
isSecurityEnabled: () => boolean;
|
||||||
};
|
};
|
||||||
indexDataEnricher: IndexDataEnricher;
|
indexDataEnricher: IndexDataEnricher;
|
||||||
lib: {
|
lib: {
|
||||||
isEsError: typeof isEsError;
|
|
||||||
parseEsError: typeof parseEsError;
|
|
||||||
handleEsError: typeof handleEsError;
|
handleEsError: typeof handleEsError;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CallAsCurrentUser = LegacyScopedClusterClient['callAsCurrentUser'];
|
|
||||||
|
|
||||||
export interface DataManagementContext {
|
|
||||||
client: ILegacyScopedClusterClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export interface IndexManagementApiRequestHandlerContext {
|
|
||||||
client: ILegacyScopedClusterClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export interface IndexManagementRequestHandlerContext extends RequestHandlerContext {
|
|
||||||
dataManagement: IndexManagementApiRequestHandlerContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export type IndexManagementRouter = IRouter<IndexManagementRequestHandlerContext>;
|
|
||||||
|
|
|
@ -5,20 +5,19 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { IScopedClusterClient } from 'kibana/server';
|
||||||
import { Index } from '../../../plugins/index_management/server';
|
import { Index } from '../../../plugins/index_management/server';
|
||||||
|
|
||||||
export const rollupDataEnricher = async (indicesList: Index[], callWithRequest: any) => {
|
export const rollupDataEnricher = async (indicesList: Index[], client: IScopedClusterClient) => {
|
||||||
if (!indicesList || !indicesList.length) {
|
if (!indicesList || !indicesList.length) {
|
||||||
return Promise.resolve(indicesList);
|
return Promise.resolve(indicesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
|
||||||
path: '/_all/_rollup/data',
|
|
||||||
method: 'GET',
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rollupJobData = await callWithRequest('transport.request', params);
|
const { body: rollupJobData } = await client.asCurrentUser.rollup.getRollupIndexCaps({
|
||||||
|
index: '_all',
|
||||||
|
});
|
||||||
|
|
||||||
return indicesList.map((index) => {
|
return indicesList.map((index) => {
|
||||||
const isRollupIndex = !!rollupJobData[index.name];
|
const isRollupIndex = !!rollupJobData[index.name];
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -281,8 +281,19 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
expect(body).to.eql({
|
expect(body).to.eql({
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
error: 'Not Found',
|
error: 'Not Found',
|
||||||
message:
|
message: 'component template matching [component_does_not_exist] not found',
|
||||||
'[resource_not_found_exception] component template matching [component_does_not_exist] not found',
|
attributes: {
|
||||||
|
error: {
|
||||||
|
reason: 'component template matching [component_does_not_exist] not found',
|
||||||
|
root_cause: [
|
||||||
|
{
|
||||||
|
reason: 'component template matching [component_does_not_exist] not found',
|
||||||
|
type: 'resource_not_found_exception',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'resource_not_found_exception',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -356,10 +367,19 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
const uri = `${API_BASE_PATH}/component_templates/${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}`;
|
const uri = `${API_BASE_PATH}/component_templates/${componentTemplateName},${COMPONENT_DOES_NOT_EXIST}`;
|
||||||
|
|
||||||
const { body } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200);
|
const { body } = await supertest.delete(uri).set('kbn-xsrf', 'xxx').expect(200);
|
||||||
|
|
||||||
expect(body.itemsDeleted).to.eql([componentTemplateName]);
|
expect(body.itemsDeleted).to.eql([componentTemplateName]);
|
||||||
expect(body.errors[0].name).to.eql(COMPONENT_DOES_NOT_EXIST);
|
expect(body.errors[0].name).to.eql(COMPONENT_DOES_NOT_EXIST);
|
||||||
expect(body.errors[0].error.msg).to.contain('resource_not_found_exception');
|
|
||||||
|
expect(body.errors[0].error.payload.attributes.error).to.eql({
|
||||||
|
root_cause: [
|
||||||
|
{
|
||||||
|
type: 'resource_not_found_exception',
|
||||||
|
reason: 'component_does_not_exist',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
type: 'resource_not_found_exception',
|
||||||
|
reason: 'component_does_not_exist',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,11 @@ import { DataStream } from '../../../../../plugins/index_management/common';
|
||||||
|
|
||||||
export default function ({ getService }: FtrProviderContext) {
|
export default function ({ getService }: FtrProviderContext) {
|
||||||
const supertest = getService('supertest');
|
const supertest = getService('supertest');
|
||||||
const es = getService('legacyEs');
|
const es = getService('es');
|
||||||
|
|
||||||
const createDataStream = async (name: string) => {
|
const createDataStream = async (name: string) => {
|
||||||
// A data stream requires an index template before it can be created.
|
// A data stream requires an index template before it can be created.
|
||||||
await es.dataManagement.saveComposableIndexTemplate({
|
await es.indices.putIndexTemplate({
|
||||||
name,
|
name,
|
||||||
body: {
|
body: {
|
||||||
// We need to match the names of backing indices with this template.
|
// We need to match the names of backing indices with this template.
|
||||||
|
@ -36,15 +36,15 @@ export default function ({ getService }: FtrProviderContext) {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await es.dataManagement.createDataStream({ name });
|
await es.indices.createDataStream({ name });
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteComposableIndexTemplate = async (name: string) => {
|
const deleteComposableIndexTemplate = async (name: string) => {
|
||||||
await es.dataManagement.deleteComposableIndexTemplate({ name });
|
await es.indices.deleteIndexTemplate({ name });
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteDataStream = async (name: string) => {
|
const deleteDataStream = async (name: string) => {
|
||||||
await es.dataManagement.deleteDataStream({ name });
|
await es.indices.deleteDataStream({ name });
|
||||||
await deleteComposableIndexTemplate(name);
|
await deleteComposableIndexTemplate(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -56,13 +56,17 @@ export default function ({ getService }) {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
|
|
||||||
// Make sure the index is open
|
// Make sure the index is open
|
||||||
const [cat1] = await catIndex(index);
|
const {
|
||||||
|
body: [cat1],
|
||||||
|
} = await catIndex(index);
|
||||||
expect(cat1.status).to.be('open');
|
expect(cat1.status).to.be('open');
|
||||||
|
|
||||||
await closeIndex(index).expect(200);
|
await closeIndex(index).expect(200);
|
||||||
|
|
||||||
// Make sure the index has been closed
|
// Make sure the index has been closed
|
||||||
const [cat2] = await catIndex(index);
|
const {
|
||||||
|
body: [cat2],
|
||||||
|
} = await catIndex(index);
|
||||||
expect(cat2.status).to.be('close');
|
expect(cat2.status).to.be('close');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -78,13 +82,17 @@ export default function ({ getService }) {
|
||||||
await closeIndex(index);
|
await closeIndex(index);
|
||||||
|
|
||||||
// Make sure the index is closed
|
// Make sure the index is closed
|
||||||
const [cat1] = await catIndex(index);
|
const {
|
||||||
|
body: [cat1],
|
||||||
|
} = await catIndex(index);
|
||||||
expect(cat1.status).to.be('close');
|
expect(cat1.status).to.be('close');
|
||||||
|
|
||||||
await openIndex(index).expect(200);
|
await openIndex(index).expect(200);
|
||||||
|
|
||||||
// Make sure the index is opened
|
// Make sure the index is opened
|
||||||
const [cat2] = await catIndex(index);
|
const {
|
||||||
|
body: [cat2],
|
||||||
|
} = await catIndex(index);
|
||||||
expect(cat2.status).to.be('open');
|
expect(cat2.status).to.be('open');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -93,12 +101,12 @@ export default function ({ getService }) {
|
||||||
it('should delete an index', async () => {
|
it('should delete an index', async () => {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
|
|
||||||
const indices1 = await catIndex(undefined, 'i');
|
const { body: indices1 } = await catIndex(undefined, 'i');
|
||||||
expect(indices1.map((index) => index.i)).to.contain(index);
|
expect(indices1.map((index) => index.i)).to.contain(index);
|
||||||
|
|
||||||
await deleteIndex([index]).expect(200);
|
await deleteIndex([index]).expect(200);
|
||||||
|
|
||||||
const indices2 = await catIndex(undefined, 'i');
|
const { body: indices2 } = await catIndex(undefined, 'i');
|
||||||
expect(indices2.map((index) => index.i)).not.to.contain(index);
|
expect(indices2.map((index) => index.i)).not.to.contain(index);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -112,12 +120,16 @@ export default function ({ getService }) {
|
||||||
it('should flush an index', async () => {
|
it('should flush an index', async () => {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
|
|
||||||
const { indices: indices1 } = await indexStats(index, 'flush');
|
const {
|
||||||
|
body: { indices: indices1 },
|
||||||
|
} = await indexStats(index, 'flush');
|
||||||
expect(indices1[index].total.flush.total).to.be(0);
|
expect(indices1[index].total.flush.total).to.be(0);
|
||||||
|
|
||||||
await flushIndex(index).expect(200);
|
await flushIndex(index).expect(200);
|
||||||
|
|
||||||
const { indices: indices2 } = await indexStats(index, 'flush');
|
const {
|
||||||
|
body: { indices: indices2 },
|
||||||
|
} = await indexStats(index, 'flush');
|
||||||
expect(indices2[index].total.flush.total).to.be(1);
|
expect(indices2[index].total.flush.total).to.be(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -126,12 +138,16 @@ export default function ({ getService }) {
|
||||||
it('should refresh an index', async () => {
|
it('should refresh an index', async () => {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
|
|
||||||
const { indices: indices1 } = await indexStats(index, 'refresh');
|
const {
|
||||||
|
body: { indices: indices1 },
|
||||||
|
} = await indexStats(index, 'refresh');
|
||||||
const previousRefreshes = indices1[index].total.refresh.total;
|
const previousRefreshes = indices1[index].total.refresh.total;
|
||||||
|
|
||||||
await refreshIndex(index).expect(200);
|
await refreshIndex(index).expect(200);
|
||||||
|
|
||||||
const { indices: indices2 } = await indexStats(index, 'refresh');
|
const {
|
||||||
|
body: { indices: indices2 },
|
||||||
|
} = await indexStats(index, 'refresh');
|
||||||
expect(indices2[index].total.refresh.total).to.be(previousRefreshes + 1);
|
expect(indices2[index].total.refresh.total).to.be(previousRefreshes + 1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -153,12 +169,16 @@ export default function ({ getService }) {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
// "sth" correspond to search throttling. Frozen indices are normal indices
|
// "sth" correspond to search throttling. Frozen indices are normal indices
|
||||||
// with search throttling turned on.
|
// with search throttling turned on.
|
||||||
const [cat1] = await catIndex(index, 'sth');
|
const {
|
||||||
|
body: [cat1],
|
||||||
|
} = await catIndex(index, 'sth');
|
||||||
expect(cat1.sth).to.be('false');
|
expect(cat1.sth).to.be('false');
|
||||||
|
|
||||||
await freeze(index).expect(200);
|
await freeze(index).expect(200);
|
||||||
|
|
||||||
const [cat2] = await catIndex(index, 'sth');
|
const {
|
||||||
|
body: [cat2],
|
||||||
|
} = await catIndex(index, 'sth');
|
||||||
expect(cat2.sth).to.be('true');
|
expect(cat2.sth).to.be('true');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -168,11 +188,15 @@ export default function ({ getService }) {
|
||||||
const index = await createIndex();
|
const index = await createIndex();
|
||||||
|
|
||||||
await freeze(index).expect(200);
|
await freeze(index).expect(200);
|
||||||
const [cat1] = await catIndex(index, 'sth');
|
const {
|
||||||
|
body: [cat1],
|
||||||
|
} = await catIndex(index, 'sth');
|
||||||
expect(cat1.sth).to.be('true');
|
expect(cat1.sth).to.be('true');
|
||||||
|
|
||||||
await unfreeze(index).expect(200);
|
await unfreeze(index).expect(200);
|
||||||
const [cat2] = await catIndex(index, 'sth');
|
const {
|
||||||
|
body: [cat2],
|
||||||
|
} = await catIndex(index, 'sth');
|
||||||
expect(cat2.sth).to.be('false');
|
expect(cat2.sth).to.be('false');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { getRandomString } from './random';
|
||||||
* @param {ElasticsearchClient} es The Elasticsearch client instance
|
* @param {ElasticsearchClient} es The Elasticsearch client instance
|
||||||
*/
|
*/
|
||||||
export const initElasticsearchHelpers = (getService) => {
|
export const initElasticsearchHelpers = (getService) => {
|
||||||
const es = getService('legacyEs');
|
const es = getService('es');
|
||||||
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
const esDeleteAllIndices = getService('esDeleteAllIndices');
|
||||||
|
|
||||||
let indicesCreated = [];
|
let indicesCreated = [];
|
||||||
|
@ -42,11 +42,11 @@ export const initElasticsearchHelpers = (getService) => {
|
||||||
componentTemplatesCreated.push(componentTemplate.name);
|
componentTemplatesCreated.push(componentTemplate.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return es.dataManagement.saveComponentTemplate(componentTemplate);
|
return es.cluster.putComponentTemplate(componentTemplate);
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteComponentTemplate = (componentTemplateName) => {
|
const deleteComponentTemplate = (componentTemplateName) => {
|
||||||
return es.dataManagement.deleteComponentTemplate({ name: componentTemplateName });
|
return es.cluster.deleteComponentTemplate({ name: componentTemplateName });
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanUpComponentTemplates = () =>
|
const cleanUpComponentTemplates = () =>
|
||||||
|
|
|
@ -193,8 +193,8 @@ export default function ({ getService }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse the ES error and return the cause', async () => {
|
it('should parse the ES error and return the cause', async () => {
|
||||||
const templateName = `template-${getRandomString()}`;
|
const templateName = `template-create-parse-es-error}`;
|
||||||
const payload = getTemplatePayload(templateName, [getRandomString()]);
|
const payload = getTemplatePayload(templateName, ['create-parse-es-error']);
|
||||||
const runtime = {
|
const runtime = {
|
||||||
myRuntimeField: {
|
myRuntimeField: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
@ -207,9 +207,9 @@ export default function ({ getService }) {
|
||||||
const { body } = await createTemplate(payload).expect(400);
|
const { body } = await createTemplate(payload).expect(400);
|
||||||
|
|
||||||
expect(body.attributes).an('object');
|
expect(body.attributes).an('object');
|
||||||
expect(body.attributes.message).contain('template after composition is invalid');
|
expect(body.attributes.error.reason).contain('template after composition is invalid');
|
||||||
// one of the item of the cause array should point to our script
|
// one of the item of the cause array should point to our script
|
||||||
expect(body.attributes.cause.join(',')).contain('"hello with error');
|
expect(body.attributes.causes.join(',')).contain('"hello with error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ export default function ({ getService }) {
|
||||||
|
|
||||||
await createTemplate(indexTemplate).expect(200);
|
await createTemplate(indexTemplate).expect(200);
|
||||||
|
|
||||||
let catTemplateResponse = await catTemplate(templateName);
|
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||||
|
|
||||||
const { name, version } = indexTemplate;
|
const { name, version } = indexTemplate;
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ export default function ({ getService }) {
|
||||||
200
|
200
|
||||||
);
|
);
|
||||||
|
|
||||||
catTemplateResponse = await catTemplate(templateName);
|
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
||||||
|
@ -247,7 +247,7 @@ export default function ({ getService }) {
|
||||||
|
|
||||||
await createTemplate(legacyIndexTemplate).expect(200);
|
await createTemplate(legacyIndexTemplate).expect(200);
|
||||||
|
|
||||||
let catTemplateResponse = await catTemplate(templateName);
|
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||||
|
|
||||||
const { name, version } = legacyIndexTemplate;
|
const { name, version } = legacyIndexTemplate;
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ export default function ({ getService }) {
|
||||||
templateName
|
templateName
|
||||||
).expect(200);
|
).expect(200);
|
||||||
|
|
||||||
catTemplateResponse = await catTemplate(templateName);
|
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
catTemplateResponse.find(({ name: templateName }) => templateName === name).version
|
||||||
|
@ -270,8 +270,8 @@ export default function ({ getService }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse the ES error and return the cause', async () => {
|
it('should parse the ES error and return the cause', async () => {
|
||||||
const templateName = `template-${getRandomString()}`;
|
const templateName = `template-update-parse-es-error}`;
|
||||||
const payload = getTemplatePayload(templateName, [getRandomString()]);
|
const payload = getTemplatePayload(templateName, ['update-parse-es-error']);
|
||||||
const runtime = {
|
const runtime = {
|
||||||
myRuntimeField: {
|
myRuntimeField: {
|
||||||
type: 'keyword',
|
type: 'keyword',
|
||||||
|
@ -292,7 +292,7 @@ export default function ({ getService }) {
|
||||||
|
|
||||||
expect(body.attributes).an('object');
|
expect(body.attributes).an('object');
|
||||||
// one of the item of the cause array should point to our script
|
// one of the item of the cause array should point to our script
|
||||||
expect(body.attributes.cause.join(',')).contain('"hello with error');
|
expect(body.attributes.causes.join(',')).contain('"hello with error');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -306,7 +306,7 @@ export default function ({ getService }) {
|
||||||
throw new Error(`Error creating template: ${createStatus} ${createBody.message}`);
|
throw new Error(`Error creating template: ${createStatus} ${createBody.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let catTemplateResponse = await catTemplate(templateName);
|
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
catTemplateResponse.find((template) => template.name === payload.name).name
|
catTemplateResponse.find((template) => template.name === payload.name).name
|
||||||
|
@ -322,7 +322,7 @@ export default function ({ getService }) {
|
||||||
expect(deleteBody.errors).to.be.empty;
|
expect(deleteBody.errors).to.be.empty;
|
||||||
expect(deleteBody.templatesDeleted[0]).to.equal(templateName);
|
expect(deleteBody.templatesDeleted[0]).to.equal(templateName);
|
||||||
|
|
||||||
catTemplateResponse = await catTemplate(templateName);
|
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||||
|
|
||||||
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
||||||
undefined
|
undefined
|
||||||
|
@ -335,7 +335,7 @@ export default function ({ getService }) {
|
||||||
|
|
||||||
await createTemplate(payload).expect(200);
|
await createTemplate(payload).expect(200);
|
||||||
|
|
||||||
let catTemplateResponse = await catTemplate(templateName);
|
let { body: catTemplateResponse } = await catTemplate(templateName);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
catTemplateResponse.find((template) => template.name === payload.name).name
|
catTemplateResponse.find((template) => template.name === payload.name).name
|
||||||
|
@ -348,7 +348,7 @@ export default function ({ getService }) {
|
||||||
expect(body.errors).to.be.empty;
|
expect(body.errors).to.be.empty;
|
||||||
expect(body.templatesDeleted[0]).to.equal(templateName);
|
expect(body.templatesDeleted[0]).to.equal(templateName);
|
||||||
|
|
||||||
catTemplateResponse = await catTemplate(templateName);
|
({ body: catTemplateResponse } = await catTemplate(templateName));
|
||||||
|
|
||||||
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
expect(catTemplateResponse.find((template) => template.name === payload.name)).to.equal(
|
||||||
undefined
|
undefined
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { format as formatUrl } from 'url';
|
||||||
|
|
||||||
import * as legacyElasticsearch from 'elasticsearch';
|
import * as legacyElasticsearch from 'elasticsearch';
|
||||||
|
|
||||||
import { elasticsearchJsPlugin as indexManagementEsClientPlugin } from '../../../plugins/index_management/server/client/elasticsearch';
|
|
||||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||||
import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config';
|
import { DEFAULT_API_VERSION } from '../../../../src/core/server/elasticsearch/elasticsearch_config';
|
||||||
|
|
||||||
|
@ -20,6 +19,5 @@ export function LegacyEsProvider({ getService }) {
|
||||||
apiVersion: DEFAULT_API_VERSION,
|
apiVersion: DEFAULT_API_VERSION,
|
||||||
host: formatUrl(config.get('servers.elasticsearch')),
|
host: formatUrl(config.get('servers.elasticsearch')),
|
||||||
requestTimeout: config.get('timeouts.esRequestTimeout'),
|
requestTimeout: config.get('timeouts.esRequestTimeout'),
|
||||||
plugins: [indexManagementEsClientPlugin],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue