Skip to content

Commit

Permalink
Merge branch 'master' into fix/flaky-graph-53749
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Jul 27, 2020
2 parents dcd6a4e + 9656cbc commit 0fb0e02
Show file tree
Hide file tree
Showing 77 changed files with 2,581 additions and 1,933 deletions.
6 changes: 5 additions & 1 deletion docs/developer/advanced/running-elasticsearch.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ This will run a snapshot of {es} that is usually built nightly. Read more about
----
yarn es snapshot
----
By default, two users are added to Elasticsearch:

- A superuser with username: `elastic` and password: `changeme`, which can be used to log into Kibana with.
- A user with username: `kibana_system` and password `changeme`. This account is used by the Kibana server to authenticate itself to Elasticsearch, and to perform certain actions on behalf of the end user. These credentials should be specified in your kibana.yml as described in <<using-kibana-with-security>>

See all available options, like how to specify a specific license, with the `--help` flag.

Expand Down Expand Up @@ -115,4 +119,4 @@ PUT _cluster/settings
}
----

Follow the cross-cluster search instructions for setting up index patterns to search across clusters (<<management-cross-cluster-search>>).
Follow the cross-cluster search instructions for setting up index patterns to search across clusters (<<management-cross-cluster-search>>).
4 changes: 2 additions & 2 deletions packages/kbn-spec-to-console/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ At the root of the Kibana repository, run the following commands:

```sh
# OSS
yarn spec_to_console -g "<ELASTICSEARCH-REPO-FOLDER>/rest-api-spec/src/main/resources/rest-api-spec/api/*" -d "src/plugins/console/server/lib/spec_definitions/json"
yarn spec_to_console -g "<ELASTICSEARCH-REPO-FOLDER>/rest-api-spec/src/main/resources/rest-api-spec/api/*" -d "src/plugins/console/server/lib/spec_definitions/json/generated"

# X-pack
yarn spec_to_console -g "<ELASTICSEARCH-REPO-FOLDER>/x-pack/plugin/src/test/resources/rest-api-spec/api/*" -d "x-pack/plugins/console_extensions/server/lib/spec_definitions/json"
yarn spec_to_console -g "<ELASTICSEARCH-REPO-FOLDER>/x-pack/plugin/src/test/resources/rest-api-spec/api/*" -d "x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated"
```

### Information used in Console that is not available in the REST spec
Expand Down
30 changes: 22 additions & 8 deletions src/core/server/elasticsearch/client/configure_client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,26 +118,40 @@ describe('configureClient', () => {
});

describe('Client logging', () => {
it('logs error when the client emits an error', () => {
it('logs error when the client emits an @elastic/elasticsearch error', () => {
const client = configureClient(config, { logger, scoped: false });

const response = createApiResponse({ body: {} });
client.emit('response', new errors.TimeoutError('message', response), response);

expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
"[TimeoutError]: message",
],
]
`);
});

it('logs error when the client emits an ResponseError returned by elasticsearch', () => {
const client = configureClient(config, { logger, scoped: false });

const response = createApiResponse({
statusCode: 400,
headers: {},
body: {
error: {
type: 'error message',
type: 'illegal_argument_exception',
reason: 'request [/_path] contains unrecognized parameter: [name]',
},
},
});
client.emit('response', new errors.ResponseError(response), null);
client.emit('response', new Error('some error'), null);
client.emit('response', new errors.ResponseError(response), response);

expect(loggingSystemMock.collect(logger).error).toMatchInlineSnapshot(`
Array [
Array [
"ResponseError: error message",
],
Array [
"Error: some error",
"[illegal_argument_exception]: request [/_path] contains unrecognized parameter: [name]",
],
]
`);
Expand Down
13 changes: 10 additions & 3 deletions src/core/server/elasticsearch/client/configure_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { stringify } from 'querystring';
import { Client } from '@elastic/elasticsearch';
import { Logger } from '../../logging';
import { parseClientOptions, ElasticsearchClientConfig } from './client_config';
import { isResponseError } from './errors';

export const configureClient = (
config: ElasticsearchClientConfig,
Expand All @@ -35,9 +36,15 @@ export const configureClient = (
};

const addLogging = (client: Client, logger: Logger, logQueries: boolean) => {
client.on('response', (err, event) => {
if (err) {
logger.error(`${err.name}: ${err.message}`);
client.on('response', (error, event) => {
if (error) {
const errorMessage =
// error details for response errors provided by elasticsearch
isResponseError(error)
? `[${event.body.error.type}]: ${event.body.error.reason}`
: `[${error.name}]: ${error.message}`;

logger.error(errorMessage);
}
if (event && logQueries) {
const params = event.meta.request.params;
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/elasticsearch/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

export { ElasticsearchClient } from './types';
export * from './types';
export { IScopedClusterClient, ScopedClusterClient } from './scoped_cluster_client';
export { ElasticsearchClientConfig } from './client_config';
export { IClusterClient, ICustomClusterClient, ClusterClient } from './cluster_client';
Expand Down
34 changes: 22 additions & 12 deletions src/core/server/elasticsearch/client/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const createInternalClientMock = (): DeeplyMockedKeys<Client> => {
.forEach((key) => {
const propType = typeof obj[key];
if (propType === 'function') {
obj[key] = jest.fn();
obj[key] = jest.fn(() => createSuccessTransportRequestPromise({}));
} else if (propType === 'object' && obj[key] != null) {
mockify(obj[key]);
}
Expand All @@ -70,6 +70,7 @@ const createInternalClientMock = (): DeeplyMockedKeys<Client> => {
return (mock as unknown) as DeeplyMockedKeys<Client>;
};

// TODO fix naming ElasticsearchClientMock
export type ElasticSearchClientMock = DeeplyMockedKeys<ElasticsearchClient>;

const createClientMock = (): ElasticSearchClientMock =>
Expand Down Expand Up @@ -124,32 +125,41 @@ export type MockedTransportRequestPromise<T> = TransportRequestPromise<T> & {
abort: jest.MockedFunction<() => undefined>;
};

const createMockedClientResponse = <T>(body: T): MockedTransportRequestPromise<ApiResponse<T>> => {
const response: ApiResponse<T> = {
body,
statusCode: 200,
warnings: [],
headers: {},
meta: {} as any,
};
const createSuccessTransportRequestPromise = <T>(
body: T,
{ statusCode = 200 }: { statusCode?: number } = {}
): MockedTransportRequestPromise<ApiResponse<T>> => {
const response = createApiResponse({ body, statusCode });
const promise = Promise.resolve(response);
(promise as MockedTransportRequestPromise<ApiResponse<T>>).abort = jest.fn();

return promise as MockedTransportRequestPromise<ApiResponse<T>>;
};

const createMockedClientError = (err: any): MockedTransportRequestPromise<never> => {
const createErrorTransportRequestPromise = (err: any): MockedTransportRequestPromise<never> => {
const promise = Promise.reject(err);
(promise as MockedTransportRequestPromise<never>).abort = jest.fn();
return promise as MockedTransportRequestPromise<never>;
};

function createApiResponse(opts: Partial<ApiResponse> = {}): ApiResponse {
return {
body: {},
statusCode: 200,
headers: {},
warnings: [],
meta: {} as any,
...opts,
};
}

export const elasticsearchClientMock = {
createClusterClient: createClusterClientMock,
createCustomClusterClient: createCustomClusterClientMock,
createScopedClusterClient: createScopedClusterClientMock,
createElasticSearchClient: createClientMock,
createInternalClient: createInternalClientMock,
createClientResponse: createMockedClientResponse,
createClientError: createMockedClientError,
createSuccessTransportRequestPromise,
createErrorTransportRequestPromise,
createApiResponse,
};
35 changes: 26 additions & 9 deletions src/core/server/elasticsearch/client/retry_call_cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import { loggingSystemMock } from '../../logging/logging_system.mock';
import { retryCallCluster, migrationRetryCallCluster } from './retry_call_cluster';

const dummyBody = { foo: 'bar' };
const createErrorReturn = (err: any) => elasticsearchClientMock.createClientError(err);
const createErrorReturn = (err: any) =>
elasticsearchClientMock.createErrorTransportRequestPromise(err);

describe('retryCallCluster', () => {
let client: ReturnType<typeof elasticsearchClientMock.createElasticSearchClient>;
Expand All @@ -33,7 +34,9 @@ describe('retryCallCluster', () => {
});

it('returns response from ES API call in case of success', async () => {
const successReturn = elasticsearchClientMock.createClientResponse({ ...dummyBody });
const successReturn = elasticsearchClientMock.createSuccessTransportRequestPromise({
...dummyBody,
});

client.asyncSearch.get.mockReturnValue(successReturn);

Expand All @@ -42,7 +45,9 @@ describe('retryCallCluster', () => {
});

it('retries ES API calls that rejects with `NoLivingConnectionsError`', async () => {
const successReturn = elasticsearchClientMock.createClientResponse({ ...dummyBody });
const successReturn = elasticsearchClientMock.createSuccessTransportRequestPromise({
...dummyBody,
});

client.asyncSearch.get
.mockImplementationOnce(() =>
Expand All @@ -57,7 +62,9 @@ describe('retryCallCluster', () => {
it('rejects when ES API calls reject with other errors', async () => {
client.ping
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);

await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot(
`[Error: unknown error]`
Expand All @@ -73,7 +80,9 @@ describe('retryCallCluster', () => {
createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any))
)
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);

await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot(
`[Error: unknown error]`
Expand All @@ -94,7 +103,9 @@ describe('migrationRetryCallCluster', () => {
client.ping
.mockImplementationOnce(() => createErrorReturn(error))
.mockImplementationOnce(() => createErrorReturn(error))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);
};

it('retries ES API calls that rejects with `NoLivingConnectionsError`', async () => {
Expand Down Expand Up @@ -225,7 +236,9 @@ describe('migrationRetryCallCluster', () => {
} as any)
)
)
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);

await migrationRetryCallCluster(() => client.ping(), logger, 1);

Expand Down Expand Up @@ -258,7 +271,9 @@ describe('migrationRetryCallCluster', () => {
} as any)
)
)
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);

await expect(
migrationRetryCallCluster(() => client.ping(), logger, 1)
Expand All @@ -274,7 +289,9 @@ describe('migrationRetryCallCluster', () => {
createErrorReturn(new errors.TimeoutError('timeout error', {} as any))
)
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
.mockImplementationOnce(() =>
elasticsearchClientMock.createSuccessTransportRequestPromise({ ...dummyBody })
);

await expect(
migrationRetryCallCluster(() => client.ping(), logger, 1)
Expand Down
2 changes: 1 addition & 1 deletion src/core/server/elasticsearch/client/retry_call_cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const retryResponseStatuses = [
403, // AuthenticationException
408, // RequestTimeout
410, // Gone
];
] as const;

/**
* Retries the provided Elasticsearch API call when a `NoLivingConnectionsError` error is
Expand Down
80 changes: 80 additions & 0 deletions src/core/server/elasticsearch/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,83 @@ export type ElasticsearchClient = Omit<
): TransportRequestPromise<ApiResponse>;
};
};

interface ShardsResponse {
total: number;
successful: number;
failed: number;
skipped: number;
}

interface Explanation {
value: number;
description: string;
details: Explanation[];
}

interface ShardsInfo {
total: number;
successful: number;
skipped: number;
failed: number;
}

export interface CountResponse {
_shards: ShardsInfo;
count: number;
}

/**
* Maintained until elasticsearch provides response typings out of the box
* https://github.com/elastic/elasticsearch-js/pull/970
*/
export interface SearchResponse<T = unknown> {
took: number;
timed_out: boolean;
_scroll_id?: string;
_shards: ShardsResponse;
hits: {
total: number;
max_score: number;
hits: Array<{
_index: string;
_type: string;
_id: string;
_score: number;
_source: T;
_version?: number;
_explanation?: Explanation;
fields?: any;
highlight?: any;
inner_hits?: any;
matched_queries?: string[];
sort?: string[];
}>;
};
aggregations?: any;
}

export interface GetResponse<T> {
_index: string;
_type: string;
_id: string;
_version: number;
_routing?: string;
found: boolean;
_source: T;
_seq_no: number;
_primary_term: number;
}

export interface DeleteDocumentResponse {
_shards: ShardsResponse;
found: boolean;
_index: string;
_type: string;
_id: string;
_version: number;
result: string;
error?: {
type: string;
};
}
Loading

0 comments on commit 0fb0e02

Please sign in to comment.