diff --git a/src/core_plugins/elasticsearch/lib/__tests__/ensure_allow_explicit_index.js b/src/core_plugins/elasticsearch/lib/__tests__/ensure_allow_explicit_index.js new file mode 100644 index 0000000000000..ac86c4ec68ef0 --- /dev/null +++ b/src/core_plugins/elasticsearch/lib/__tests__/ensure_allow_explicit_index.js @@ -0,0 +1,75 @@ +import sinon from 'sinon'; +import expect from 'expect.js'; + +import { ensureAllowExplicitIndex } from '../ensure_allow_explicit_index'; + +const createStubCallWithInternal = responders => ( + sinon.spy(async (params) => { + if (responders.length) { + return await responders.shift()(params); + } + + throw new Error('Unexpected client.mget call'); + }) +); + +const createStubConfig = () => ({ + get: sinon.spy((key) => { + switch (key) { + case 'kibana.index': return '.kibana'; + case 'pkg.version': return '0.0.0'; + default: throw new Error(`Unexpected config.get('${key}') call`); + } + }) +}); + +describe('ensureAllowExplicitIndex()', () => { + it('attempts an mget with index in request', async () => { + const config = createStubConfig(); + const callWithInternalUser = createStubCallWithInternal([ + () => ({ ok: true }) + ]); + + const resp = await ensureAllowExplicitIndex(callWithInternalUser, config); + expect(resp).to.be(true); + }); + + it(`reports "illegal_argument_exception" that mentions "explicit index"`, async () => { + const config = createStubConfig(); + const callWithInternalUser = createStubCallWithInternal([ + () => ({ + error: { + type: 'illegal_argument_exception', + reason: 'explicit index not supported' + } + }) + ]); + + try { + await ensureAllowExplicitIndex(callWithInternalUser, config); + throw new Error('expected ensureAllowExplicitIndex() to throw error'); + } catch (error) { + expect(error.message).to.contain('rest.action.multi.allow_explicit_index'); + } + }); + + it('reports unexpected errors', async () => { + const config = createStubConfig(); + const callWithInternalUser = createStubCallWithInternal([ + () => ({ + error: { + type: 'foo', + reason: 'bar' + } + }) + ]); + + try { + await ensureAllowExplicitIndex(callWithInternalUser, config); + throw new Error('expected ensureAllowExplicitIndex() to throw error'); + } catch (error) { + expect(error.message).to.contain('[foo]'); + expect(error.message).to.contain('bar'); + } + }); +}); diff --git a/src/core_plugins/elasticsearch/lib/__tests__/health_check.js b/src/core_plugins/elasticsearch/lib/__tests__/health_check.js index 016b22c164733..9bdde587f78e9 100644 --- a/src/core_plugins/elasticsearch/lib/__tests__/health_check.js +++ b/src/core_plugins/elasticsearch/lib/__tests__/health_check.js @@ -38,6 +38,7 @@ describe('plugins/elasticsearch', () => { cluster = { callWithInternalUser: sinon.stub() }; cluster.callWithInternalUser.withArgs('index', sinon.match.any).returns(Promise.resolve()); + cluster.callWithInternalUser.withArgs('mget', sinon.match.any).returns(Promise.resolve({ ok: true })); cluster.callWithInternalUser.withArgs('get', sinon.match.any).returns(Promise.resolve({ found: false })); cluster.callWithInternalUser.withArgs('search', sinon.match.any).returns(Promise.resolve({ hits: { hits: [] } })); cluster.callWithInternalUser.withArgs('nodes.info', sinon.match.any).returns(Promise.resolve({ diff --git a/src/core_plugins/elasticsearch/lib/ensure_allow_explicit_index.js b/src/core_plugins/elasticsearch/lib/ensure_allow_explicit_index.js new file mode 100644 index 0000000000000..7444b6862721c --- /dev/null +++ b/src/core_plugins/elasticsearch/lib/ensure_allow_explicit_index.js @@ -0,0 +1,36 @@ +export async function ensureAllowExplicitIndex(callWithInternalUser, config) { + const resp = await callWithInternalUser('mget', { + ignore: [400], + body: { + docs: [ + { + _index: config.get('kibana.index'), + _type: 'config', + _id: config.get('pkg.version'), + }, + ], + }, + }); + + if (!resp.error) { + return true; + } + + const error = resp.error || {}; + const errorReason = error.reason || ''; + + const isArgError = error.type === 'illegal_argument_exception'; + const isExplicitIndexException = isArgError && errorReason.includes('explicit index'); + + if (isExplicitIndexException) { + throw new Error( + 'Kibana must be able to specify the index within Elasticsearch multi-requests ' + + '(rest.action.multi.allow_explicit_index=true).' + ); + } + + throw new Error( + 'Unable to ensure that rest.action.multi.allow_explicit_index=true: ' + + `[${error.type}] ${errorReason}` + ); +} diff --git a/src/core_plugins/elasticsearch/lib/health_check.js b/src/core_plugins/elasticsearch/lib/health_check.js index 6b5e5f4085ea6..8bd4e877939fe 100644 --- a/src/core_plugins/elasticsearch/lib/health_check.js +++ b/src/core_plugins/elasticsearch/lib/health_check.js @@ -6,6 +6,7 @@ import createKibanaIndex from './create_kibana_index'; import kibanaVersion from './kibana_version'; import { ensureEsVersion } from './ensure_es_version'; import { ensureNotTribe } from './ensure_not_tribe'; +import { ensureAllowExplicitIndex } from './ensure_allow_explicit_index'; const NoConnections = elasticsearch.errors.NoConnections; import util from 'util'; @@ -94,7 +95,8 @@ module.exports = function (plugin, server) { const healthCheck = waitForPong(callAdminAsKibanaUser, config.get('elasticsearch.url')) .then(waitForEsVersion) - .then(ensureNotTribe.bind(this, callAdminAsKibanaUser)) + .then(() => ensureNotTribe(callAdminAsKibanaUser)) + .then(() => ensureAllowExplicitIndex(callAdminAsKibanaUser, config)) .then(waitForShards) .then(_.partial(migrateConfig, server)) .then(() => {