diff --git a/x-pack/plugins/apm/typings/numeral.d.ts b/x-pack/plugins/apm/typings/numeral.d.ts index f08f99f5ef11a1..da2d84662a09fa 100644 --- a/x-pack/plugins/apm/typings/numeral.d.ts +++ b/x-pack/plugins/apm/typings/numeral.d.ts @@ -7,6 +7,7 @@ interface Numeral { (value?: unknown): Numeral; format: (pattern: string) => string; + unformat: (pattern: string) => number; } declare var numeral: Numeral; diff --git a/x-pack/plugins/reporting/index.js b/x-pack/plugins/reporting/index.js index 1ef8edbc5f921b..014a38bc2066b8 100644 --- a/x-pack/plugins/reporting/index.js +++ b/x-pack/plugins/reporting/index.js @@ -15,6 +15,7 @@ import { createQueueFactory } from './server/lib/create_queue'; import { config as appConfig } from './server/config/config'; import { checkLicenseFactory } from './server/lib/check_license'; import { validateConfig } from './server/lib/validate_config'; +import { validateMaxContentLength } from './server/lib/validate_max_content_length'; import { exportTypesRegistryFactory } from './server/lib/export_types_registry'; import { PHANTOM, createBrowserDriverFactory, getDefaultBrowser, getDefaultChromiumSandboxDisabled } from './server/browsers'; import { logConfiguration } from './log_configuration'; @@ -148,7 +149,10 @@ export const reporting = (kibana) => { server.expose('exportTypesRegistry', exportTypesRegistry); const config = server.config(); - validateConfig(config, message => server.log(['reporting', 'warning'], message)); + const logWarning = message => server.log(['reporting', 'warning'], message); + + validateConfig(config, logWarning); + validateMaxContentLength(server, logWarning); logConfiguration(config, message => server.log(['reporting', 'debug'], message)); const { xpack_main: xpackMainPlugin } = server.plugins; diff --git a/x-pack/plugins/reporting/server/lib/__tests__/validate_max_content_length.js b/x-pack/plugins/reporting/server/lib/__tests__/validate_max_content_length.js new file mode 100644 index 00000000000000..28ac7bade95c1f --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/__tests__/validate_max_content_length.js @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from 'expect.js'; +import sinon from 'sinon'; +import { validateMaxContentLength } from '../validate_max_content_length'; + +const FIVE_HUNDRED_MEGABYTES = 524288000; +const ONE_HUNDRED_MEGABYTES = 104857600; + +describe('Reporting: Validate Max Content Length', () => { + const log = sinon.spy(); + + beforeEach(() => { + log.resetHistory(); + }); + + it('should log warning messages when reporting has a higher max-size than elasticsearch', async () => { + const server = { + config: () => ({ + get: sinon.stub().returns(FIVE_HUNDRED_MEGABYTES), + }), + plugins: { + elasticsearch: { + getCluster: () => ({ + callWithInternalUser: () => ({ + defaults: { + http: { + max_content_length: '100mb', + }, + }, + }), + }), + }, + }, + }; + + await validateMaxContentLength(server, log); + + sinon.assert.calledWithMatch(log, `xpack.reporting.csv.maxSizeBytes (524288000) is higher`); + sinon.assert.calledWithMatch(log, `than ElasticSearch's http.max_content_length (104857600)`); + sinon.assert.calledWithMatch(log, 'Please set http.max_content_length in ElasticSearch to match'); + sinon.assert.calledWithMatch(log, 'or lower your xpack.reporting.csv.maxSizeBytes in Kibana'); + }); + + it('should do nothing when reporting has the same max-size as elasticsearch', async () => { + const server = { + config: () => ({ + get: sinon.stub().returns(ONE_HUNDRED_MEGABYTES), + }), + plugins: { + elasticsearch: { + getCluster: () => ({ + callWithInternalUser: () => ({ + defaults: { + http: { + max_content_length: '100mb', + }, + }, + }), + }), + }, + }, + }; + + expect(async () => validateMaxContentLength(server, log)).not.to.throwError(); + sinon.assert.notCalled(log); + }); +}); diff --git a/x-pack/plugins/reporting/server/lib/validate_max_content_length.ts b/x-pack/plugins/reporting/server/lib/validate_max_content_length.ts new file mode 100644 index 00000000000000..0142b482bc0aaf --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/validate_max_content_length.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import numeral from '@elastic/numeral'; +import { defaults, get } from 'lodash'; +const KIBANA_MAX_SIZE_BYTES_PATH = 'xpack.reporting.csv.maxSizeBytes'; +const ES_MAX_SIZE_BYTES_PATH = 'http.max_content_length'; + +export async function validateMaxContentLength(server: any, log: (message: string) => any) { + const config = server.config(); + const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('data'); + + const elasticClusterSettingsResponse = await callWithInternalUser('cluster.getSettings', { + includeDefaults: true, + }); + const { persistent, transient, defaults: defaultSettings } = elasticClusterSettingsResponse; + const elasticClusterSettings = defaults({}, persistent, transient, defaultSettings); + + const elasticSearchMaxContent = get(elasticClusterSettings, 'http.max_content_length', '100mb'); + const elasticSearchMaxContentBytes = numeral().unformat(elasticSearchMaxContent.toUpperCase()); + const kibanaMaxContentBytes = config.get(KIBANA_MAX_SIZE_BYTES_PATH); + + if (kibanaMaxContentBytes > elasticSearchMaxContentBytes) { + log( + `${KIBANA_MAX_SIZE_BYTES_PATH} (${kibanaMaxContentBytes}) is higher than ElasticSearch's ${ES_MAX_SIZE_BYTES_PATH} (${elasticSearchMaxContentBytes}). ` + + `Please set ${ES_MAX_SIZE_BYTES_PATH} in ElasticSearch to match, or lower your ${KIBANA_MAX_SIZE_BYTES_PATH} in Kibana to avoid this warning.` + ); + } +}