diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
index 2398410fa4b84..90aa2f0100d88 100644
--- a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md
@@ -16,5 +16,6 @@ export interface ElasticsearchStatusMeta
| Property | Type | Description |
| --- | --- | --- |
| [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md) | NodesVersionCompatibility['incompatibleNodes']
| |
+| [nodesInfoRequestError](./kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md) | NodesVersionCompatibility['nodesInfoRequestError']
| |
| [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md) | NodesVersionCompatibility['warningNodes']
| |
diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md
new file mode 100644
index 0000000000000..1b46078a1a453
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [nodesInfoRequestError](./kibana-plugin-core-server.elasticsearchstatusmeta.nodesinforequesterror.md)
+
+## ElasticsearchStatusMeta.nodesInfoRequestError property
+
+Signature:
+
+```typescript
+nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
+```
diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
index 6fcfacc3bc908..cbdac9d5455b0 100644
--- a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
+++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md
@@ -18,5 +18,6 @@ export interface NodesVersionCompatibility
| [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md) | boolean
| |
| [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md) | string
| |
| [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md) | string
| |
+| [nodesInfoRequestError](./kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md) | Error
| |
| [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md) | NodeInfo[]
| |
diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md
new file mode 100644
index 0000000000000..aa9421afed6e8
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [nodesInfoRequestError](./kibana-plugin-core-server.nodesversioncompatibility.nodesinforequesterror.md)
+
+## NodesVersionCompatibility.nodesInfoRequestError property
+
+Signature:
+
+```typescript
+nodesInfoRequestError?: Error;
+```
diff --git a/src/core/server/elasticsearch/status.test.ts b/src/core/server/elasticsearch/status.test.ts
index 6f21fc204a1c2..c1f7cf0e35892 100644
--- a/src/core/server/elasticsearch/status.test.ts
+++ b/src/core/server/elasticsearch/status.test.ts
@@ -54,7 +54,7 @@ describe('calculateStatus', () => {
});
});
- it('changes to available with a differemnt message when isCompatible and warningNodes present', async () => {
+ it('changes to available with a different message when isCompatible and warningNodes present', async () => {
expect(
await calculateStatus$(
of({
@@ -204,4 +204,117 @@ describe('calculateStatus', () => {
]
`);
});
+
+ it('emits status updates when node info request error changes', () => {
+ const nodeCompat$ = new Subject();
+
+ const statusUpdates: ServiceStatus[] = [];
+ const subscription = calculateStatus$(nodeCompat$).subscribe((status) =>
+ statusUpdates.push(status)
+ );
+
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. connect ECONNREFUSED',
+ nodesInfoRequestError: new Error('connect ECONNREFUSED'),
+ });
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. security_exception',
+ nodesInfoRequestError: new Error('security_exception'),
+ });
+
+ subscription.unsubscribe();
+ expect(statusUpdates).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "level": unavailable,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Waiting for Elasticsearch",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: connect ECONNREFUSED],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. connect ECONNREFUSED",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: security_exception],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. security_exception",
+ },
+ ]
+ `);
+ });
+
+ it('changes to available when a request error is resolved', () => {
+ const nodeCompat$ = new Subject();
+
+ const statusUpdates: ServiceStatus[] = [];
+ const subscription = calculateStatus$(nodeCompat$).subscribe((status) =>
+ statusUpdates.push(status)
+ );
+
+ nodeCompat$.next({
+ isCompatible: false,
+ kibanaVersion: '1.1.1',
+ incompatibleNodes: [],
+ warningNodes: [],
+ message: 'Unable to retrieve version info. security_exception',
+ nodesInfoRequestError: new Error('security_exception'),
+ });
+ nodeCompat$.next({
+ isCompatible: true,
+ kibanaVersion: '1.1.1',
+ warningNodes: [],
+ incompatibleNodes: [],
+ });
+
+ subscription.unsubscribe();
+ expect(statusUpdates).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "level": unavailable,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Waiting for Elasticsearch",
+ },
+ Object {
+ "level": critical,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "nodesInfoRequestError": [Error: security_exception],
+ "warningNodes": Array [],
+ },
+ "summary": "Unable to retrieve version info. security_exception",
+ },
+ Object {
+ "level": available,
+ "meta": Object {
+ "incompatibleNodes": Array [],
+ "warningNodes": Array [],
+ },
+ "summary": "Elasticsearch is available",
+ },
+ ]
+ `);
+ });
});
diff --git a/src/core/server/elasticsearch/status.ts b/src/core/server/elasticsearch/status.ts
index 68a61b07f498e..23e44b71863f1 100644
--- a/src/core/server/elasticsearch/status.ts
+++ b/src/core/server/elasticsearch/status.ts
@@ -32,6 +32,7 @@ export const calculateStatus$ = (
message,
incompatibleNodes,
warningNodes,
+ nodesInfoRequestError,
}): ServiceStatus => {
if (!isCompatible) {
return {
@@ -40,7 +41,11 @@ export const calculateStatus$ = (
// Message should always be present, but this is a safe fallback
message ??
`Some Elasticsearch nodes are not compatible with this version of Kibana`,
- meta: { warningNodes, incompatibleNodes },
+ meta: {
+ warningNodes,
+ incompatibleNodes,
+ ...(nodesInfoRequestError && { nodesInfoRequestError }),
+ },
};
} else if (warningNodes.length > 0) {
return {
diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts
index 85678c21f03b0..8bbf665cbc096 100644
--- a/src/core/server/elasticsearch/types.ts
+++ b/src/core/server/elasticsearch/types.ts
@@ -179,6 +179,7 @@ export type InternalElasticsearchServiceStart = ElasticsearchServiceStart;
export interface ElasticsearchStatusMeta {
warningNodes: NodesVersionCompatibility['warningNodes'];
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
+ nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
}
/**
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
index 0e08fd2ddc4c5..70166704679fe 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.test.ts
@@ -19,7 +19,8 @@ const mockLogger = mockLoggerFactory.get('mock logger');
const KIBANA_VERSION = '5.1.0';
const createEsSuccess = elasticsearchClientMock.createSuccessTransportRequestPromise;
-const createEsError = elasticsearchClientMock.createErrorTransportRequestPromise;
+const createEsErrorReturn = (err: any) =>
+ elasticsearchClientMock.createErrorTransportRequestPromise(err);
function createNodes(...versions: string[]): NodesInfo {
const nodes = {} as any;
@@ -102,6 +103,28 @@ describe('mapNodesVersionCompatibility', () => {
`"You're running Kibana 5.1.0 with some different versions of Elasticsearch. Update Kibana or Elasticsearch to the same version to prevent compatibility issues: v5.1.1 @ http_address (ip)"`
);
});
+
+ it('returns isCompatible=false without an extended message when a nodesInfoRequestError is not provided', async () => {
+ const result = mapNodesVersionCompatibility({ nodes: {} }, KIBANA_VERSION, false);
+ expect(result.isCompatible).toBe(false);
+ expect(result.nodesInfoRequestError).toBeUndefined();
+ expect(result.message).toMatchInlineSnapshot(
+ `"Unable to retrieve version information from Elasticsearch nodes."`
+ );
+ });
+
+ it('returns isCompatible=false with an extended message when a nodesInfoRequestError is present', async () => {
+ const result = mapNodesVersionCompatibility(
+ { nodes: {}, nodesInfoRequestError: new Error('connection refused') },
+ KIBANA_VERSION,
+ false
+ );
+ expect(result.isCompatible).toBe(false);
+ expect(result.nodesInfoRequestError).toBeTruthy();
+ expect(result.message).toMatchInlineSnapshot(
+ `"Unable to retrieve version information from Elasticsearch nodes. connection refused"`
+ );
+ });
});
describe('pollEsNodesVersion', () => {
@@ -119,10 +142,10 @@ describe('pollEsNodesVersion', () => {
internalClient.nodes.info.mockImplementationOnce(() => createEsSuccess(infos));
};
const nodeInfosErrorOnce = (error: any) => {
- internalClient.nodes.info.mockImplementationOnce(() => createEsError(error));
+ internalClient.nodes.info.mockImplementationOnce(() => createEsErrorReturn(new Error(error)));
};
- it('returns iscCompatible=false and keeps polling when a poll request throws', (done) => {
+ it('returns isCompatible=false and keeps polling when a poll request throws', (done) => {
expect.assertions(3);
const expectedCompatibilityResults = [false, false, true];
jest.clearAllMocks();
@@ -148,6 +171,100 @@ describe('pollEsNodesVersion', () => {
});
});
+ it('returns the error from a failed nodes.info call when a poll request throws', (done) => {
+ expect.assertions(2);
+ const expectedCompatibilityResults = [false];
+ const expectedMessageResults = [
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosErrorOnce('mock request error');
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(1))
+ .subscribe({
+ next: (result) => {
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ expect(result.message).toBe(expectedMessageResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
+ it('only emits if the error from a failed nodes.info call changed from the previous poll', (done) => {
+ expect.assertions(4);
+ const expectedCompatibilityResults = [false, false];
+ const expectedMessageResults = [
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error 2',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosErrorOnce('mock request error'); // emit
+ nodeInfosErrorOnce('mock request error'); // ignore, same error message
+ nodeInfosErrorOnce('mock request error 2'); // emit
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(2))
+ .subscribe({
+ next: (result) => {
+ expect(result.message).toBe(expectedMessageResults.shift());
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
+ it('returns isCompatible=false and keeps polling when a poll request throws, only responding again if the error message has changed', (done) => {
+ expect.assertions(8);
+ const expectedCompatibilityResults = [false, false, true, false];
+ const expectedMessageResults = [
+ 'This version of Kibana (v5.1.0) is incompatible with the following Elasticsearch nodes in your cluster: v5.0.0 @ http_address (ip)',
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ "You're running Kibana 5.1.0 with some different versions of Elasticsearch. Update Kibana or Elasticsearch to the same version to prevent compatibility issues: v5.2.0 @ http_address (ip), v5.1.1-Beta1 @ http_address (ip)",
+ 'Unable to retrieve version information from Elasticsearch nodes. mock request error',
+ ];
+ jest.clearAllMocks();
+
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.0.0')); // emit
+ nodeInfosErrorOnce('mock request error'); // emit
+ nodeInfosErrorOnce('mock request error'); // ignore
+ nodeInfosSuccessOnce(createNodes('5.1.0', '5.2.0', '5.1.1-Beta1')); // emit
+ nodeInfosErrorOnce('mock request error'); // emit
+
+ pollEsNodesVersion({
+ internalClient,
+ esVersionCheckInterval: 1,
+ ignoreVersionMismatch: false,
+ kibanaVersion: KIBANA_VERSION,
+ log: mockLogger,
+ })
+ .pipe(take(4))
+ .subscribe({
+ next: (result) => {
+ expect(result.isCompatible).toBe(expectedCompatibilityResults.shift());
+ expect(result.message).toBe(expectedMessageResults.shift());
+ },
+ complete: done,
+ error: done,
+ });
+ });
+
it('returns compatibility results', (done) => {
expect.assertions(1);
const nodes = createNodes('5.1.0', '5.2.0', '5.0.0');
diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
index fb7ef0583e4a4..43cd52f1b5721 100644
--- a/src/core/server/elasticsearch/version_check/ensure_es_version.ts
+++ b/src/core/server/elasticsearch/version_check/ensure_es_version.ts
@@ -49,6 +49,7 @@ export interface NodesVersionCompatibility {
incompatibleNodes: NodeInfo[];
warningNodes: NodeInfo[];
kibanaVersion: string;
+ nodesInfoRequestError?: Error;
}
function getHumanizedNodeName(node: NodeInfo) {
@@ -57,22 +58,28 @@ function getHumanizedNodeName(node: NodeInfo) {
}
export function mapNodesVersionCompatibility(
- nodesInfo: NodesInfo,
+ nodesInfoResponse: NodesInfo & { nodesInfoRequestError?: Error },
kibanaVersion: string,
ignoreVersionMismatch: boolean
): NodesVersionCompatibility {
- if (Object.keys(nodesInfo.nodes ?? {}).length === 0) {
+ if (Object.keys(nodesInfoResponse.nodes ?? {}).length === 0) {
+ // Note: If the a nodesInfoRequestError is present, the message contains the nodesInfoRequestError.message as a suffix
+ let message = `Unable to retrieve version information from Elasticsearch nodes.`;
+ if (nodesInfoResponse.nodesInfoRequestError) {
+ message = message + ` ${nodesInfoResponse.nodesInfoRequestError.message}`;
+ }
return {
isCompatible: false,
- message: 'Unable to retrieve version information from Elasticsearch nodes.',
+ message,
incompatibleNodes: [],
warningNodes: [],
kibanaVersion,
+ nodesInfoRequestError: nodesInfoResponse.nodesInfoRequestError,
};
}
- const nodes = Object.keys(nodesInfo.nodes)
+ const nodes = Object.keys(nodesInfoResponse.nodes)
.sort() // Sorting ensures a stable node ordering for comparison
- .map((key) => nodesInfo.nodes[key])
+ .map((key) => nodesInfoResponse.nodes[key])
.map((node) => Object.assign({}, node, { name: getHumanizedNodeName(node) }));
// Aggregate incompatible ES nodes.
@@ -112,7 +119,13 @@ export function mapNodesVersionCompatibility(
kibanaVersion,
};
}
-
+// Returns true if NodesVersionCompatibility nodesInfoRequestError is the same
+function compareNodesInfoErrorMessages(
+ prev: NodesVersionCompatibility,
+ curr: NodesVersionCompatibility
+): boolean {
+ return prev.nodesInfoRequestError?.message === curr.nodesInfoRequestError?.message;
+}
// Returns true if two NodesVersionCompatibility entries match
function compareNodes(prev: NodesVersionCompatibility, curr: NodesVersionCompatibility) {
const nodesEqual = (n: NodeInfo, m: NodeInfo) => n.ip === m.ip && n.version === m.version;
@@ -121,7 +134,8 @@ function compareNodes(prev: NodesVersionCompatibility, curr: NodesVersionCompati
curr.incompatibleNodes.length === prev.incompatibleNodes.length &&
curr.warningNodes.length === prev.warningNodes.length &&
curr.incompatibleNodes.every((node, i) => nodesEqual(node, prev.incompatibleNodes[i])) &&
- curr.warningNodes.every((node, i) => nodesEqual(node, prev.warningNodes[i]))
+ curr.warningNodes.every((node, i) => nodesEqual(node, prev.warningNodes[i])) &&
+ compareNodesInfoErrorMessages(curr, prev)
);
}
@@ -141,14 +155,14 @@ export const pollEsNodesVersion = ({
})
).pipe(
map(({ body }) => body),
- catchError((_err) => {
- return of({ nodes: {} });
+ catchError((nodesInfoRequestError) => {
+ return of({ nodes: {}, nodesInfoRequestError });
})
);
}),
- map((nodesInfo: NodesInfo) =>
- mapNodesVersionCompatibility(nodesInfo, kibanaVersion, ignoreVersionMismatch)
+ map((nodesInfoResponse: NodesInfo & { nodesInfoRequestError?: Error }) =>
+ mapNodesVersionCompatibility(nodesInfoResponse, kibanaVersion, ignoreVersionMismatch)
),
- distinctUntilChanged(compareNodes) // Only emit if there are new nodes or versions
+ distinctUntilChanged(compareNodes) // Only emit if there are new nodes or versions or if we return an error and that error changes
);
};
diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md
index 4c12ca53b9098..f4c70d718bc87 100644
--- a/src/core/server/server.api.md
+++ b/src/core/server/server.api.md
@@ -976,6 +976,8 @@ export interface ElasticsearchStatusMeta {
// (undocumented)
incompatibleNodes: NodesVersionCompatibility['incompatibleNodes'];
// (undocumented)
+ nodesInfoRequestError?: NodesVersionCompatibility['nodesInfoRequestError'];
+ // (undocumented)
warningNodes: NodesVersionCompatibility['warningNodes'];
}
@@ -1727,6 +1729,8 @@ export interface NodesVersionCompatibility {
// (undocumented)
message?: string;
// (undocumented)
+ nodesInfoRequestError?: Error;
+ // (undocumented)
warningNodes: NodeInfo[];
}