Skip to content

Commit

Permalink
Merge branch 'master' into implement/es-archiver-package
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Jul 20, 2020
2 parents d6e06a2 + b29e8ee commit 749edda
Show file tree
Hide file tree
Showing 52 changed files with 2,258 additions and 1,363 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@
"@types/browserslist-useragent": "^3.0.0",
"@types/chance": "^1.0.0",
"@types/cheerio": "^0.22.10",
"@types/chromedriver": "^2.38.0",
"@types/chromedriver": "^81.0.0",
"@types/classnames": "^2.2.9",
"@types/color": "^3.0.0",
"@types/d3": "^3.5.43",
Expand Down Expand Up @@ -412,7 +412,7 @@
"chai": "3.5.0",
"chance": "1.0.18",
"cheerio": "0.22.0",
"chromedriver": "^83.0.0",
"chromedriver": "^84.0.0",
"classnames": "2.2.6",
"dedent": "^0.7.0",
"delete-empty": "^2.0.0",
Expand Down
3 changes: 1 addition & 2 deletions src/core/public/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
@import './chrome/index';
@import './overlays/index';
@import './rendering/index';
@import './styles/index';

// Global styles need to be migrated
@import '../../legacy/ui/public/styles/_legacy/_index';
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
@import '@elastic/eui/src/components/call_out/variables';
@import '@elastic/eui/src/components/call_out/mixins';

// SASSTODO: Replace with an EUI editor
// Intentionally not using the EuiCodeBlock colors here because they actually change
// hue from light to dark theme. So some colors would change while others wouldn't.
Expand Down Expand Up @@ -181,7 +178,7 @@
}

&.ace_multiselect .ace_selection.ace_start {
box-shadow: 0 0 3px 0px $euiColorEmptyShade;
box-shadow: 0 0 3px 0 $euiColorEmptyShade;
}

.ace_marker-layer .ace_step {
Expand Down
58 changes: 58 additions & 0 deletions src/core/public/styles/_base.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
@import '@elastic/eui/src/components/collapsible_nav/variables';
// Application Layout

// chrome-context
// TODO #64541
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) .content {
display: flex;
flex-flow: row nowrap;
width: 100%;
height: 100%;
overflow: hidden;
}

.application,
.app-container {
> * {
position: relative;
}
}

.application {
position: relative;
z-index: 0;
display: flex;
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
flex-direction: column;

> * {
flex-shrink: 0;
}
}

// We apply brute force focus states to anything not coming from Eui
// which has focus states designed at the component level.
// You can also use "kbn-resetFocusState" to not apply the default focus
// state. This is useful when you've already hand crafted your own
// focus states in Kibana.
:focus {
&:not([class^='eui']):not(.kbn-resetFocusState) {
@include euiFocusRing;
}
}

// A necessary hack so that the above focus policy doesn't pollute some EUI
// entrenched inputs.
.euiComboBox {
// :not() specificity needed to override the above
input:not([class^='eui']):focus {
animation: none !important;
}
}

.euiBody--collapsibleNavIsDocked .euiBottomBar {
margin-left: $euiCollapsibleNavWidth;
}
2 changes: 2 additions & 0 deletions src/core/public/styles/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import './base';
@import './ace_overrides';
1 change: 1 addition & 0 deletions src/core/server/elasticsearch/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export { IScopedClusterClient, ScopedClusterClient } from './scoped_cluster_clie
export { ElasticsearchClientConfig } from './client_config';
export { IClusterClient, ICustomClusterClient, ClusterClient } from './cluster_client';
export { configureClient } from './configure_client';
export { retryCallCluster, migrationRetryCallCluster } from './retry_call_cluster';
283 changes: 283 additions & 0 deletions src/core/server/elasticsearch/client/retry_call_cluster.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { errors } from '@elastic/elasticsearch';
import { elasticsearchClientMock } from './mocks';
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);

describe('retryCallCluster', () => {
let client: ReturnType<typeof elasticsearchClientMock.createElasticSearchClient>;

beforeEach(() => {
client = elasticsearchClientMock.createElasticSearchClient();
});

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

client.asyncSearch.get.mockReturnValue(successReturn);

const result = await retryCallCluster(() => client.asyncSearch.get());
expect(result.body).toEqual(dummyBody);
});

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

client.asyncSearch.get
.mockImplementationOnce(() =>
createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any))
)
.mockImplementationOnce(() => successReturn);

const result = await retryCallCluster(() => client.asyncSearch.get());
expect(result.body).toEqual(dummyBody);
});

it('rejects when ES API calls reject with other errors', async () => {
client.ping
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));

await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot(
`[Error: unknown error]`
);
});

it('stops retrying when ES API calls reject with other errors', async () => {
client.ping
.mockImplementationOnce(() =>
createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any))
)
.mockImplementationOnce(() =>
createErrorReturn(new errors.NoLivingConnectionsError('no living connections', {} as any))
)
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));

await expect(retryCallCluster(() => client.ping())).rejects.toMatchInlineSnapshot(
`[Error: unknown error]`
);
});
});

describe('migrationRetryCallCluster', () => {
let client: ReturnType<typeof elasticsearchClientMock.createElasticSearchClient>;
let logger: ReturnType<typeof loggingSystemMock.createLogger>;

beforeEach(() => {
client = elasticsearchClientMock.createElasticSearchClient();
logger = loggingSystemMock.createLogger();
});

const mockClientPingWithErrorBeforeSuccess = (error: any) => {
client.ping
.mockImplementationOnce(() => createErrorReturn(error))
.mockImplementationOnce(() => createErrorReturn(error))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));
};

it('retries ES API calls that rejects with `NoLivingConnectionsError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.NoLivingConnectionsError('no living connections', {} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with `ConnectionError`', async () => {
mockClientPingWithErrorBeforeSuccess(new errors.ConnectionError('connection error', {} as any));

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with `TimeoutError`', async () => {
mockClientPingWithErrorBeforeSuccess(new errors.TimeoutError('timeout error', {} as any));

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with 503 `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 503,
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects 401 `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 401,
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with 403 `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 403,
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with 408 `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 408,
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with 410 `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 410,
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('retries ES API calls that rejects with `snapshot_in_progress_exception` `ResponseError`', async () => {
mockClientPingWithErrorBeforeSuccess(
new errors.ResponseError({
statusCode: 500,
body: {
error: {
type: 'snapshot_in_progress_exception',
},
},
} as any)
);

const result = await migrationRetryCallCluster(() => client.ping(), logger, 1);
expect(result.body).toEqual(dummyBody);
});

it('logs only once for each unique error message', async () => {
client.ping
.mockImplementationOnce(() =>
createErrorReturn(
new errors.ResponseError({
statusCode: 503,
} as any)
)
)
.mockImplementationOnce(() =>
createErrorReturn(new errors.ConnectionError('connection error', {} as any))
)
.mockImplementationOnce(() =>
createErrorReturn(
new errors.ResponseError({
statusCode: 503,
} as any)
)
)
.mockImplementationOnce(() =>
createErrorReturn(new errors.ConnectionError('connection error', {} as any))
)
.mockImplementationOnce(() =>
createErrorReturn(
new errors.ResponseError({
statusCode: 500,
body: {
error: {
type: 'snapshot_in_progress_exception',
},
},
} as any)
)
)
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));

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

expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(`
Array [
Array [
"Unable to connect to Elasticsearch. Error: Response Error",
],
Array [
"Unable to connect to Elasticsearch. Error: connection error",
],
Array [
"Unable to connect to Elasticsearch. Error: snapshot_in_progress_exception",
],
]
`);
});

it('rejects when ES API calls reject with other errors', async () => {
client.ping
.mockImplementationOnce(() =>
createErrorReturn(
new errors.ResponseError({
statusCode: 418,
body: {
error: {
type: `I'm a teapot`,
},
},
} as any)
)
)
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));

await expect(
migrationRetryCallCluster(() => client.ping(), logger, 1)
).rejects.toMatchInlineSnapshot(`[ResponseError: I'm a teapot]`);
});

it('stops retrying when ES API calls reject with other errors', async () => {
client.ping
.mockImplementationOnce(() =>
createErrorReturn(new errors.TimeoutError('timeout error', {} as any))
)
.mockImplementationOnce(() =>
createErrorReturn(new errors.TimeoutError('timeout error', {} as any))
)
.mockImplementationOnce(() => createErrorReturn(new Error('unknown error')))
.mockImplementationOnce(() => elasticsearchClientMock.createClientResponse({ ...dummyBody }));

await expect(
migrationRetryCallCluster(() => client.ping(), logger, 1)
).rejects.toMatchInlineSnapshot(`[Error: unknown error]`);
});
});
Loading

0 comments on commit 749edda

Please sign in to comment.