mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Reporting] Switch Serverside Config Wrapper to NP (#62500)
* New config * fix translations json * add csv.useByteOrderMarkEncoding to schema * imports cleanup * restore "get default chromium sandbox disabled" functionality * integrate getDefaultChromiumSandboxDisabled * fix tests * --wip-- [skip ci] * add more schema tests * diff prettiness * trash legacy files that moved to NP * create_config tests * Hoist create_config * better disableSandbox tests * fix ts * fix export * fix bad code * make comments better * fix i18n * comment * automatically setting... logs * replace log_configuration * fix lint * This is f2 * improve startup log about sandbox info * update docs with log reference * revert log removal Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
23e3f1aab5
commit
f4c81b440d
28 changed files with 709 additions and 842 deletions
|
@ -11,12 +11,12 @@ sandboxing techniques differ for each operating system.
|
|||
The Linux sandbox depends on user namespaces, which were introduced with the 3.8 Linux kernel. However, many
|
||||
distributions don't have user namespaces enabled by default, or they require the CAP_SYS_ADMIN capability. {reporting}
|
||||
will automatically disable the sandbox when it is running on Debian and CentOS as additional steps are required to enable
|
||||
unprivileged usernamespaces. In these situations, you'll see the following message in your {kib} logs:
|
||||
`Enabling the Chromium sandbox provides an additional layer of protection`.
|
||||
unprivileged usernamespaces. In these situations, you'll see the following message in your {kib} startup logs:
|
||||
`Chromium sandbox provides an additional layer of protection, but is not supported for your OS.
|
||||
Automatically setting 'xpack.reporting.capture.browser.chromium.disableSandbox: true'.`
|
||||
|
||||
If your kernel is 3.8 or newer, it's
|
||||
recommended to enable usernamespaces and set `xpack.reporting.capture.browser.chromium.disableSandbox: false` in your
|
||||
`kibana.yml` to enable the sandbox.
|
||||
Reporting will automatically enable the Chromium sandbox at startup when a supported OS is detected. However, if your kernel is 3.8 or newer, it's
|
||||
recommended to set `xpack.reporting.capture.browser.chromium.disableSandbox: false` in your `kibana.yml` to explicitly enable usernamespaces.
|
||||
|
||||
==== Docker
|
||||
When running {kib} in a Docker container, all container processes are run within a usernamespace with seccomp-bpf and
|
||||
|
|
|
@ -1,387 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`config schema with context {"dev":false,"dist":false} produces correct config 1`] = `
|
||||
Object {
|
||||
"capture": Object {
|
||||
"browser": Object {
|
||||
"autoDownload": true,
|
||||
"chromium": Object {
|
||||
"disableSandbox": "<platform dependent>",
|
||||
"maxScreenshotDimension": 1950,
|
||||
"proxy": Object {
|
||||
"enabled": false,
|
||||
},
|
||||
},
|
||||
"type": "chromium",
|
||||
},
|
||||
"concurrency": 4,
|
||||
"loadDelay": 3000,
|
||||
"maxAttempts": 1,
|
||||
"networkPolicy": Object {
|
||||
"enabled": true,
|
||||
"rules": Array [
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "http:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "https:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "ws:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "wss:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "data:",
|
||||
},
|
||||
Object {
|
||||
"allow": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"settleTime": 1000,
|
||||
"timeout": 20000,
|
||||
"timeouts": Object {
|
||||
"openUrl": 30000,
|
||||
"renderComplete": 30000,
|
||||
"waitForElements": 30000,
|
||||
},
|
||||
"viewport": Object {
|
||||
"height": 1200,
|
||||
"width": 1950,
|
||||
},
|
||||
"zoom": 2,
|
||||
},
|
||||
"csv": Object {
|
||||
"checkForFormulas": true,
|
||||
"enablePanelActionDownload": true,
|
||||
"maxSizeBytes": 10485760,
|
||||
"scroll": Object {
|
||||
"duration": "30s",
|
||||
"size": 500,
|
||||
},
|
||||
"useByteOrderMarkEncoding": false,
|
||||
},
|
||||
"enabled": true,
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"index": ".reporting",
|
||||
"kibanaServer": Object {},
|
||||
"poll": Object {
|
||||
"jobCompletionNotifier": Object {
|
||||
"interval": 10000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
"jobsRefresh": Object {
|
||||
"interval": 5000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
},
|
||||
"queue": Object {
|
||||
"indexInterval": "week",
|
||||
"pollEnabled": true,
|
||||
"pollInterval": 3000,
|
||||
"pollIntervalErrorMultiplier": 10,
|
||||
"timeout": 120000,
|
||||
},
|
||||
"roles": Object {
|
||||
"allow": Array [
|
||||
"reporting_user",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`config schema with context {"dev":false,"dist":true} produces correct config 1`] = `
|
||||
Object {
|
||||
"capture": Object {
|
||||
"browser": Object {
|
||||
"autoDownload": false,
|
||||
"chromium": Object {
|
||||
"disableSandbox": "<platform dependent>",
|
||||
"maxScreenshotDimension": 1950,
|
||||
"proxy": Object {
|
||||
"enabled": false,
|
||||
},
|
||||
},
|
||||
"type": "chromium",
|
||||
},
|
||||
"concurrency": 4,
|
||||
"loadDelay": 3000,
|
||||
"maxAttempts": 3,
|
||||
"networkPolicy": Object {
|
||||
"enabled": true,
|
||||
"rules": Array [
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "http:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "https:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "ws:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "wss:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "data:",
|
||||
},
|
||||
Object {
|
||||
"allow": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"settleTime": 1000,
|
||||
"timeout": 20000,
|
||||
"timeouts": Object {
|
||||
"openUrl": 30000,
|
||||
"renderComplete": 30000,
|
||||
"waitForElements": 30000,
|
||||
},
|
||||
"viewport": Object {
|
||||
"height": 1200,
|
||||
"width": 1950,
|
||||
},
|
||||
"zoom": 2,
|
||||
},
|
||||
"csv": Object {
|
||||
"checkForFormulas": true,
|
||||
"enablePanelActionDownload": true,
|
||||
"maxSizeBytes": 10485760,
|
||||
"scroll": Object {
|
||||
"duration": "30s",
|
||||
"size": 500,
|
||||
},
|
||||
"useByteOrderMarkEncoding": false,
|
||||
},
|
||||
"enabled": true,
|
||||
"index": ".reporting",
|
||||
"kibanaServer": Object {},
|
||||
"poll": Object {
|
||||
"jobCompletionNotifier": Object {
|
||||
"interval": 10000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
"jobsRefresh": Object {
|
||||
"interval": 5000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
},
|
||||
"queue": Object {
|
||||
"indexInterval": "week",
|
||||
"pollEnabled": true,
|
||||
"pollInterval": 3000,
|
||||
"pollIntervalErrorMultiplier": 10,
|
||||
"timeout": 120000,
|
||||
},
|
||||
"roles": Object {
|
||||
"allow": Array [
|
||||
"reporting_user",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`config schema with context {"dev":true,"dist":false} produces correct config 1`] = `
|
||||
Object {
|
||||
"capture": Object {
|
||||
"browser": Object {
|
||||
"autoDownload": true,
|
||||
"chromium": Object {
|
||||
"disableSandbox": "<platform dependent>",
|
||||
"maxScreenshotDimension": 1950,
|
||||
"proxy": Object {
|
||||
"enabled": false,
|
||||
},
|
||||
},
|
||||
"type": "chromium",
|
||||
},
|
||||
"concurrency": 4,
|
||||
"loadDelay": 3000,
|
||||
"maxAttempts": 1,
|
||||
"networkPolicy": Object {
|
||||
"enabled": true,
|
||||
"rules": Array [
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "http:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "https:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "ws:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "wss:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "data:",
|
||||
},
|
||||
Object {
|
||||
"allow": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"settleTime": 1000,
|
||||
"timeout": 20000,
|
||||
"timeouts": Object {
|
||||
"openUrl": 30000,
|
||||
"renderComplete": 30000,
|
||||
"waitForElements": 30000,
|
||||
},
|
||||
"viewport": Object {
|
||||
"height": 1200,
|
||||
"width": 1950,
|
||||
},
|
||||
"zoom": 2,
|
||||
},
|
||||
"csv": Object {
|
||||
"checkForFormulas": true,
|
||||
"enablePanelActionDownload": true,
|
||||
"maxSizeBytes": 10485760,
|
||||
"scroll": Object {
|
||||
"duration": "30s",
|
||||
"size": 500,
|
||||
},
|
||||
"useByteOrderMarkEncoding": false,
|
||||
},
|
||||
"enabled": true,
|
||||
"encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"index": ".reporting",
|
||||
"kibanaServer": Object {},
|
||||
"poll": Object {
|
||||
"jobCompletionNotifier": Object {
|
||||
"interval": 10000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
"jobsRefresh": Object {
|
||||
"interval": 5000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
},
|
||||
"queue": Object {
|
||||
"indexInterval": "week",
|
||||
"pollEnabled": true,
|
||||
"pollInterval": 3000,
|
||||
"pollIntervalErrorMultiplier": 10,
|
||||
"timeout": 120000,
|
||||
},
|
||||
"roles": Object {
|
||||
"allow": Array [
|
||||
"reporting_user",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`config schema with context {"dev":true,"dist":true} produces correct config 1`] = `
|
||||
Object {
|
||||
"capture": Object {
|
||||
"browser": Object {
|
||||
"autoDownload": false,
|
||||
"chromium": Object {
|
||||
"disableSandbox": "<platform dependent>",
|
||||
"maxScreenshotDimension": 1950,
|
||||
"proxy": Object {
|
||||
"enabled": false,
|
||||
},
|
||||
},
|
||||
"type": "chromium",
|
||||
},
|
||||
"concurrency": 4,
|
||||
"loadDelay": 3000,
|
||||
"maxAttempts": 3,
|
||||
"networkPolicy": Object {
|
||||
"enabled": true,
|
||||
"rules": Array [
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "http:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "https:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "ws:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "wss:",
|
||||
},
|
||||
Object {
|
||||
"allow": true,
|
||||
"protocol": "data:",
|
||||
},
|
||||
Object {
|
||||
"allow": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"settleTime": 1000,
|
||||
"timeout": 20000,
|
||||
"timeouts": Object {
|
||||
"openUrl": 30000,
|
||||
"renderComplete": 30000,
|
||||
"waitForElements": 30000,
|
||||
},
|
||||
"viewport": Object {
|
||||
"height": 1200,
|
||||
"width": 1950,
|
||||
},
|
||||
"zoom": 2,
|
||||
},
|
||||
"csv": Object {
|
||||
"checkForFormulas": true,
|
||||
"enablePanelActionDownload": true,
|
||||
"maxSizeBytes": 10485760,
|
||||
"scroll": Object {
|
||||
"duration": "30s",
|
||||
"size": 500,
|
||||
},
|
||||
"useByteOrderMarkEncoding": false,
|
||||
},
|
||||
"enabled": true,
|
||||
"index": ".reporting",
|
||||
"kibanaServer": Object {},
|
||||
"poll": Object {
|
||||
"jobCompletionNotifier": Object {
|
||||
"interval": 10000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
"jobsRefresh": Object {
|
||||
"interval": 5000,
|
||||
"intervalErrorMultiplier": 5,
|
||||
},
|
||||
},
|
||||
"queue": Object {
|
||||
"indexInterval": "week",
|
||||
"pollEnabled": true,
|
||||
"pollInterval": 3000,
|
||||
"pollIntervalErrorMultiplier": 10,
|
||||
"timeout": 120000,
|
||||
},
|
||||
"roles": Object {
|
||||
"allow": Array [
|
||||
"reporting_user",
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -1,183 +0,0 @@
|
|||
/*
|
||||
* 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 { BROWSER_TYPE } from './common/constants';
|
||||
// @ts-ignore untyped module
|
||||
import { config as appConfig } from './server/config/config';
|
||||
import { getDefaultChromiumSandboxDisabled } from './server/browsers';
|
||||
|
||||
export async function config(Joi: any) {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
kibanaServer: Joi.object({
|
||||
protocol: Joi.string().valid(['http', 'https']),
|
||||
hostname: Joi.string().invalid('0'),
|
||||
port: Joi.number().integer(),
|
||||
}).default(),
|
||||
queue: Joi.object({
|
||||
indexInterval: Joi.string().default('week'),
|
||||
pollEnabled: Joi.boolean().default(true),
|
||||
pollInterval: Joi.number()
|
||||
.integer()
|
||||
.default(3000),
|
||||
pollIntervalErrorMultiplier: Joi.number()
|
||||
.integer()
|
||||
.default(10),
|
||||
timeout: Joi.number()
|
||||
.integer()
|
||||
.default(120000),
|
||||
}).default(),
|
||||
capture: Joi.object({
|
||||
timeouts: Joi.object({
|
||||
openUrl: Joi.number()
|
||||
.integer()
|
||||
.default(30000),
|
||||
waitForElements: Joi.number()
|
||||
.integer()
|
||||
.default(30000),
|
||||
renderComplete: Joi.number()
|
||||
.integer()
|
||||
.default(30000),
|
||||
}).default(),
|
||||
networkPolicy: Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
rules: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
allow: Joi.boolean().required(),
|
||||
protocol: Joi.string(),
|
||||
host: Joi.string(),
|
||||
})
|
||||
)
|
||||
.default([
|
||||
{ allow: true, protocol: 'http:' },
|
||||
{ allow: true, protocol: 'https:' },
|
||||
{ allow: true, protocol: 'ws:' },
|
||||
{ allow: true, protocol: 'wss:' },
|
||||
{ allow: true, protocol: 'data:' },
|
||||
{ allow: false }, // Default action is to deny!
|
||||
]),
|
||||
}).default(),
|
||||
zoom: Joi.number()
|
||||
.integer()
|
||||
.default(2),
|
||||
viewport: Joi.object({
|
||||
width: Joi.number()
|
||||
.integer()
|
||||
.default(1950),
|
||||
height: Joi.number()
|
||||
.integer()
|
||||
.default(1200),
|
||||
}).default(),
|
||||
timeout: Joi.number()
|
||||
.integer()
|
||||
.default(20000), // deprecated
|
||||
loadDelay: Joi.number()
|
||||
.integer()
|
||||
.default(3000),
|
||||
settleTime: Joi.number()
|
||||
.integer()
|
||||
.default(1000), // deprecated
|
||||
concurrency: Joi.number()
|
||||
.integer()
|
||||
.default(appConfig.concurrency), // deprecated
|
||||
browser: Joi.object({
|
||||
type: Joi.any()
|
||||
.valid(BROWSER_TYPE)
|
||||
.default(BROWSER_TYPE),
|
||||
autoDownload: Joi.boolean().when('$dist', {
|
||||
is: true,
|
||||
then: Joi.default(false),
|
||||
otherwise: Joi.default(true),
|
||||
}),
|
||||
chromium: Joi.object({
|
||||
inspect: Joi.boolean()
|
||||
.when('$dev', {
|
||||
is: false,
|
||||
then: Joi.valid(false),
|
||||
else: Joi.default(false),
|
||||
})
|
||||
.default(),
|
||||
disableSandbox: Joi.boolean().default(await getDefaultChromiumSandboxDisabled()),
|
||||
proxy: Joi.object({
|
||||
enabled: Joi.boolean().default(false),
|
||||
server: Joi.string()
|
||||
.uri({ scheme: ['http', 'https'] })
|
||||
.when('enabled', {
|
||||
is: Joi.valid(false),
|
||||
then: Joi.valid(null),
|
||||
else: Joi.required(),
|
||||
}),
|
||||
bypass: Joi.array()
|
||||
.items(Joi.string().regex(/^[^\s]+$/))
|
||||
.when('enabled', {
|
||||
is: Joi.valid(false),
|
||||
then: Joi.valid(null),
|
||||
else: Joi.default([]),
|
||||
}),
|
||||
}).default(),
|
||||
maxScreenshotDimension: Joi.number()
|
||||
.integer()
|
||||
.default(1950),
|
||||
}).default(),
|
||||
}).default(),
|
||||
maxAttempts: Joi.number()
|
||||
.integer()
|
||||
.greater(0)
|
||||
.when('$dist', {
|
||||
is: true,
|
||||
then: Joi.default(3),
|
||||
otherwise: Joi.default(1),
|
||||
})
|
||||
.default(),
|
||||
}).default(),
|
||||
csv: Joi.object({
|
||||
useByteOrderMarkEncoding: Joi.boolean().default(false),
|
||||
checkForFormulas: Joi.boolean().default(true),
|
||||
enablePanelActionDownload: Joi.boolean().default(true),
|
||||
maxSizeBytes: Joi.number()
|
||||
.integer()
|
||||
.default(1024 * 1024 * 10), // bytes in a kB * kB in a mB * 10
|
||||
scroll: Joi.object({
|
||||
duration: Joi.string()
|
||||
.regex(/^[0-9]+(d|h|m|s|ms|micros|nanos)$/, { name: 'DurationString' })
|
||||
.default('30s'),
|
||||
size: Joi.number()
|
||||
.integer()
|
||||
.default(500),
|
||||
}).default(),
|
||||
}).default(),
|
||||
encryptionKey: Joi.when(Joi.ref('$dist'), {
|
||||
is: true,
|
||||
then: Joi.string(),
|
||||
otherwise: Joi.string().default('a'.repeat(32)),
|
||||
}),
|
||||
roles: Joi.object({
|
||||
allow: Joi.array()
|
||||
.items(Joi.string())
|
||||
.default(['reporting_user']),
|
||||
}).default(),
|
||||
index: Joi.string().default('.reporting'),
|
||||
poll: Joi.object({
|
||||
jobCompletionNotifier: Joi.object({
|
||||
interval: Joi.number()
|
||||
.integer()
|
||||
.default(10000),
|
||||
intervalErrorMultiplier: Joi.number()
|
||||
.integer()
|
||||
.default(5),
|
||||
}).default(),
|
||||
jobsRefresh: Joi.object({
|
||||
interval: Joi.number()
|
||||
.integer()
|
||||
.default(5000),
|
||||
intervalErrorMultiplier: Joi.number()
|
||||
.integer()
|
||||
.default(5),
|
||||
}).default(),
|
||||
}).default(),
|
||||
}).default();
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* 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 { reporting } from './index';
|
||||
import { getConfigSchema } from '../../../test_utils';
|
||||
|
||||
// The snapshot records the number of cpus available
|
||||
// to make the snapshot deterministic `os.cpus` needs to be mocked
|
||||
// but the other members on `os` must remain untouched
|
||||
jest.mock('os', () => {
|
||||
const os = jest.requireActual('os');
|
||||
os.cpus = () => [{}, {}, {}, {}];
|
||||
return os;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/valid-describe
|
||||
const describeWithContext = describe.each([
|
||||
[{ dev: false, dist: false }],
|
||||
[{ dev: true, dist: false }],
|
||||
[{ dev: false, dist: true }],
|
||||
[{ dev: true, dist: true }],
|
||||
]);
|
||||
|
||||
describeWithContext('config schema with context %j', context => {
|
||||
it('produces correct config', async () => {
|
||||
const schema = await getConfigSchema(reporting);
|
||||
const value: any = await schema.validate({}, { context });
|
||||
value.capture.browser.chromium.disableSandbox = '<platform dependent>';
|
||||
await expect(value).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n';
|
|||
import { Legacy } from 'kibana';
|
||||
import { resolve } from 'path';
|
||||
import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants';
|
||||
import { config as reportingConfig } from './config';
|
||||
import { legacyInit } from './server/legacy';
|
||||
import { ReportingPluginSpecOptions } from './types';
|
||||
|
||||
|
@ -17,10 +16,8 @@ const kbToBase64Length = (kb: number) => Math.floor((kb * 1024 * 8) / 6);
|
|||
export const reporting = (kibana: any) => {
|
||||
return new kibana.Plugin({
|
||||
id: PLUGIN_ID,
|
||||
configPrefix: 'xpack.reporting',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
require: ['kibana', 'elasticsearch', 'xpack_main'],
|
||||
config: reportingConfig,
|
||||
|
||||
uiExports: {
|
||||
uiSettingDefaults: {
|
||||
|
@ -47,14 +44,5 @@ export const reporting = (kibana: any) => {
|
|||
async init(server: Legacy.Server) {
|
||||
return legacyInit(server, this);
|
||||
},
|
||||
|
||||
deprecations({ unused }: any) {
|
||||
return [
|
||||
unused('capture.concurrency'),
|
||||
unused('capture.timeout'),
|
||||
unused('capture.settleTime'),
|
||||
unused('kibanaApp'),
|
||||
];
|
||||
},
|
||||
} as ReportingPluginSpecOptions);
|
||||
};
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* 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 getosSync, { LinuxOs } from 'getos';
|
||||
import { promisify } from 'util';
|
||||
import { BROWSER_TYPE } from './common/constants';
|
||||
import { CaptureConfig } from './server/types';
|
||||
import { Logger } from './types';
|
||||
|
||||
const getos = promisify(getosSync);
|
||||
|
||||
export async function logConfiguration(captureConfig: CaptureConfig, logger: Logger) {
|
||||
const {
|
||||
browser: {
|
||||
type: browserType,
|
||||
chromium: { disableSandbox },
|
||||
},
|
||||
} = captureConfig;
|
||||
|
||||
logger.debug(`Browser type: ${browserType}`);
|
||||
if (browserType === BROWSER_TYPE) {
|
||||
logger.debug(`Chromium sandbox disabled: ${disableSandbox}`);
|
||||
}
|
||||
|
||||
const os = await getos();
|
||||
const { os: osName, dist, release } = os as LinuxOs;
|
||||
if (dist) {
|
||||
logger.debug(`Running on os "${osName}", distribution "${dist}", release "${release}"`);
|
||||
} else {
|
||||
logger.debug(`Running on os "${osName}"`);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ type ViewportConfig = CaptureConfig['viewport'];
|
|||
type BrowserConfig = CaptureConfig['browser']['chromium'];
|
||||
|
||||
interface LaunchArgs {
|
||||
userDataDir: BrowserConfig['userDataDir'];
|
||||
userDataDir: string;
|
||||
viewport: ViewportConfig;
|
||||
disableSandbox: BrowserConfig['disableSandbox'];
|
||||
proxy: BrowserConfig['proxy'];
|
||||
|
|
|
@ -8,7 +8,6 @@ import * as chromiumDefinition from './chromium';
|
|||
|
||||
export { ensureAllBrowsersDownloaded } from './download';
|
||||
export { createBrowserDriverFactory } from './create_browser_driver_factory';
|
||||
export { getDefaultChromiumSandboxDisabled } from './default_chromium_sandbox_disabled';
|
||||
|
||||
export { HeadlessChromiumDriver } from './chromium/driver';
|
||||
export { HeadlessChromiumDriverFactory } from './chromium/driver_factory';
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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 { cpus } from 'os';
|
||||
|
||||
const defaultCPUCount = 2;
|
||||
|
||||
function cpuCount() {
|
||||
try {
|
||||
return cpus().length;
|
||||
} catch (e) {
|
||||
return defaultCPUCount;
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
concurrency: cpuCount(),
|
||||
};
|
|
@ -6,10 +6,9 @@
|
|||
|
||||
import { Legacy } from 'kibana';
|
||||
import { CoreSetup } from 'src/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import crypto from 'crypto';
|
||||
import { get } from 'lodash';
|
||||
import { NetworkPolicy } from '../../types';
|
||||
import { ConfigType as ReportingConfigType } from '../../../../../plugins/reporting/server';
|
||||
export { ReportingConfigType };
|
||||
|
||||
// make config.get() aware of the value type it returns
|
||||
interface Config<BaseType> {
|
||||
|
@ -56,131 +55,6 @@ export interface ReportingConfig extends Config<ReportingConfigType> {
|
|||
kbnConfig: Config<KbnServerConfigType>;
|
||||
}
|
||||
|
||||
type BrowserType = 'chromium';
|
||||
|
||||
interface BrowserConfig {
|
||||
inspect: boolean;
|
||||
userDataDir: string;
|
||||
viewport: { width: number; height: number };
|
||||
disableSandbox: boolean;
|
||||
proxy: {
|
||||
enabled: boolean;
|
||||
server?: string;
|
||||
bypass?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
interface CaptureConfig {
|
||||
browser: {
|
||||
type: BrowserType;
|
||||
autoDownload: boolean;
|
||||
chromium: BrowserConfig;
|
||||
};
|
||||
maxAttempts: number;
|
||||
networkPolicy: NetworkPolicy;
|
||||
loadDelay: number;
|
||||
timeouts: {
|
||||
openUrl: number;
|
||||
waitForElements: number;
|
||||
renderComplete: number;
|
||||
};
|
||||
viewport: any;
|
||||
zoom: any;
|
||||
}
|
||||
|
||||
interface QueueConfig {
|
||||
indexInterval: string;
|
||||
pollEnabled: boolean;
|
||||
pollInterval: number;
|
||||
pollIntervalErrorMultiplier: number;
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
interface ScrollConfig {
|
||||
duration: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface ReportingConfigType {
|
||||
capture: CaptureConfig;
|
||||
csv: {
|
||||
scroll: ScrollConfig;
|
||||
enablePanelActionDownload: boolean;
|
||||
checkForFormulas: boolean;
|
||||
maxSizeBytes: number;
|
||||
useByteOrderMarkEncoding: boolean;
|
||||
};
|
||||
encryptionKey: string;
|
||||
kibanaServer: any;
|
||||
index: string;
|
||||
queue: QueueConfig;
|
||||
roles: any;
|
||||
}
|
||||
|
||||
const addConfigDefaults = (
|
||||
server: Legacy.Server,
|
||||
core: CoreSetup,
|
||||
baseConfig: ReportingConfigType
|
||||
) => {
|
||||
// encryption key
|
||||
let encryptionKey = baseConfig.encryptionKey;
|
||||
if (encryptionKey === undefined) {
|
||||
server.log(
|
||||
['reporting', 'config', 'warning'],
|
||||
i18n.translate('xpack.reporting.selfCheckEncryptionKey.warning', {
|
||||
defaultMessage:
|
||||
`Generating a random key for {setting}. To prevent pending reports ` +
|
||||
`from failing on restart, please set {setting} in kibana.yml`,
|
||||
values: {
|
||||
setting: 'xpack.reporting.encryptionKey',
|
||||
},
|
||||
})
|
||||
);
|
||||
encryptionKey = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
const { kibanaServer: reportingServer } = baseConfig;
|
||||
const serverInfo = core.http.getServerInfo();
|
||||
|
||||
// kibanaServer.hostname, default to server.host, don't allow "0"
|
||||
let kibanaServerHostname = reportingServer.hostname ? reportingServer.hostname : serverInfo.host;
|
||||
if (kibanaServerHostname === '0') {
|
||||
server.log(
|
||||
['reporting', 'config', 'warning'],
|
||||
i18n.translate('xpack.reporting.selfCheckHostname.warning', {
|
||||
defaultMessage:
|
||||
`Found 'server.host: "0"' in settings. This is incompatible with Reporting. ` +
|
||||
`To enable Reporting to work, '{setting}: 0.0.0.0' is being automatically to the configuration. ` +
|
||||
`You can change to 'server.host: 0.0.0.0' or add '{setting}: 0.0.0.0' in kibana.yml to prevent this message.`,
|
||||
values: {
|
||||
setting: 'xpack.reporting.kibanaServer.hostname',
|
||||
},
|
||||
})
|
||||
);
|
||||
kibanaServerHostname = '0.0.0.0';
|
||||
}
|
||||
|
||||
// kibanaServer.port, default to server.port
|
||||
const kibanaServerPort = reportingServer.port
|
||||
? reportingServer.port
|
||||
: serverInfo.port; // prettier-ignore
|
||||
|
||||
// kibanaServer.protocol, default to server.protocol
|
||||
const kibanaServerProtocol = reportingServer.protocol
|
||||
? reportingServer.protocol
|
||||
: serverInfo.protocol;
|
||||
|
||||
return {
|
||||
...baseConfig,
|
||||
encryptionKey,
|
||||
kibanaServer: {
|
||||
hostname: kibanaServerHostname,
|
||||
port: kibanaServerPort,
|
||||
protocol: kibanaServerProtocol,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const buildConfig = (
|
||||
core: CoreSetup,
|
||||
server: Legacy.Server,
|
||||
|
@ -204,10 +78,8 @@ export const buildConfig = (
|
|||
},
|
||||
};
|
||||
|
||||
// spreading arguments as an array allows the return type to be known by the compiler
|
||||
reportingConfig = addConfigDefaults(server, core, reportingConfig);
|
||||
return {
|
||||
get: (...keys: string[]) => get(reportingConfig, keys.join('.'), null),
|
||||
get: (...keys: string[]) => get(reportingConfig, keys.join('.'), null), // spreading arguments as an array allows the return type to be known by the compiler
|
||||
kbnConfig: {
|
||||
get: (...keys: string[]) => get(kbnConfig, keys.join('.'), null),
|
||||
},
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
*/
|
||||
|
||||
import { Legacy } from 'kibana';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { PluginInitializerContext } from 'src/core/server';
|
||||
import { PluginsSetup } from '../../../../plugins/reporting/server';
|
||||
import { SecurityPluginSetup } from '../../../../plugins/security/server';
|
||||
import { ReportingPluginSpecOptions } from '../types';
|
||||
import { buildConfig } from './config';
|
||||
|
@ -17,7 +19,6 @@ const buildLegacyDependencies = (
|
|||
reportingPlugin: ReportingPluginSpecOptions
|
||||
): LegacySetup => ({
|
||||
route: server.route.bind(server),
|
||||
config: server.config,
|
||||
plugins: {
|
||||
xpack_main: server.plugins.xpack_main,
|
||||
reporting: reportingPlugin,
|
||||
|
@ -32,14 +33,13 @@ export const legacyInit = async (
|
|||
reportingLegacyPlugin: ReportingPluginSpecOptions
|
||||
) => {
|
||||
const { core: coreSetup } = server.newPlatform.setup;
|
||||
const legacyConfig = server.config();
|
||||
const reportingConfig = buildConfig(coreSetup, server, legacyConfig.get('xpack.reporting'));
|
||||
|
||||
const { config$ } = (server.newPlatform.setup.plugins.reporting as PluginsSetup).__legacy;
|
||||
const reportingConfig = await config$.pipe(take(1)).toPromise();
|
||||
const __LEGACY = buildLegacyDependencies(server, reportingLegacyPlugin);
|
||||
|
||||
const pluginInstance = plugin(
|
||||
server.newPlatform.coreContext as PluginInitializerContext,
|
||||
reportingConfig
|
||||
buildConfig(coreSetup, server, reportingConfig)
|
||||
);
|
||||
await pluginInstance.setup(coreSetup, {
|
||||
elasticsearch: coreSetup.elasticsearch,
|
||||
|
|
|
@ -8,6 +8,7 @@ import moment from 'moment';
|
|||
|
||||
export const intervals = ['year', 'month', 'week', 'day', 'hour', 'minute'];
|
||||
|
||||
// TODO: This helper function can be removed by using `schema.duration` objects in the reporting config schema
|
||||
export function indexTimestamp(intervalStr, separator = '-') {
|
||||
if (separator.match(/[a-z]/i)) throw new Error('Interval separator can not be a letter');
|
||||
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server';
|
||||
import { logConfiguration } from '../log_configuration';
|
||||
import { createBrowserDriverFactory } from './browsers';
|
||||
import { ReportingCore, ReportingConfig } from './core';
|
||||
import { createQueueFactory, enqueueJobFactory, LevelLogger, runValidations } from './lib';
|
||||
import { setFieldFormats } from './services';
|
||||
import { ReportingSetup, ReportingSetupDeps, ReportingStart, ReportingStartDeps } from './types';
|
||||
import { registerReportingUsageCollector } from './usage';
|
||||
// @ts-ignore no module definition
|
||||
import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status';
|
||||
|
||||
export class ReportingPlugin
|
||||
implements Plugin<ReportingSetup, ReportingStart, ReportingSetupDeps, ReportingStartDeps> {
|
||||
|
@ -61,8 +58,6 @@ export class ReportingPlugin
|
|||
|
||||
setFieldFormats(plugins.data.fieldFormats);
|
||||
|
||||
logConfiguration(this.config.get('capture'), this.logger);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/
|
|||
import { SecurityPluginSetup } from '../../../../plugins/security/server';
|
||||
import { XPackMainPlugin } from '../../xpack_main/server/xpack_main';
|
||||
import { ReportingPluginSpecOptions } from '../types';
|
||||
import { ReportingConfig, ReportingConfigType } from './core';
|
||||
import { ReportingConfigType } from './core';
|
||||
|
||||
export interface ReportingSetupDeps {
|
||||
elasticsearch: ElasticsearchServiceSetup;
|
||||
|
@ -30,7 +30,6 @@ export type ReportingSetup = object;
|
|||
export type ReportingStart = object;
|
||||
|
||||
export interface LegacySetup {
|
||||
config: Legacy.Server['config'];
|
||||
plugins: {
|
||||
xpack_main: XPackMainPlugin & {
|
||||
status?: any;
|
||||
|
|
|
@ -11,7 +11,6 @@ jest.mock('../server/browsers');
|
|||
jest.mock('../server/lib/create_queue');
|
||||
jest.mock('../server/lib/enqueue_job');
|
||||
jest.mock('../server/lib/validate');
|
||||
jest.mock('../log_configuration');
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const reportingPollConfig = {
|
||||
jobCompletionNotifier: { interval: 10000, intervalErrorMultiplier: 5 },
|
||||
jobsRefresh: { interval: 5000, intervalErrorMultiplier: 5 },
|
||||
};
|
|
@ -2,6 +2,9 @@
|
|||
"id": "reporting",
|
||||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"optionalPlugins": [
|
||||
"usageCollection"
|
||||
],
|
||||
"configPath": ["xpack", "reporting"],
|
||||
"requiredPlugins": [
|
||||
"home",
|
||||
|
@ -12,6 +15,6 @@
|
|||
"share",
|
||||
"kibanaLegacy"
|
||||
],
|
||||
"server": false,
|
||||
"server": true,
|
||||
"ui": true
|
||||
}
|
||||
|
|
165
x-pack/plugins/reporting/server/config/create_config.test.ts
Normal file
165
x-pack/plugins/reporting/server/config/create_config.test.ts
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* 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 * as Rx from 'rxjs';
|
||||
import { CoreSetup, Logger, PluginInitializerContext } from 'src/core/server';
|
||||
import { ConfigType as ReportingConfigType } from './schema';
|
||||
import { createConfig$ } from './create_config';
|
||||
|
||||
interface KibanaServer {
|
||||
host?: string;
|
||||
port?: number;
|
||||
protocol?: string;
|
||||
}
|
||||
|
||||
const makeMockInitContext = (config: {
|
||||
capture?: Partial<ReportingConfigType['capture']>;
|
||||
encryptionKey?: string;
|
||||
kibanaServer: Partial<ReportingConfigType['kibanaServer']>;
|
||||
}): PluginInitializerContext =>
|
||||
({
|
||||
config: {
|
||||
create: () =>
|
||||
Rx.of({
|
||||
...config,
|
||||
capture: config.capture || { browser: { chromium: { disableSandbox: false } } },
|
||||
kibanaServer: config.kibanaServer || {},
|
||||
}),
|
||||
},
|
||||
} as PluginInitializerContext);
|
||||
|
||||
const makeMockCoreSetup = (serverInfo: KibanaServer): CoreSetup =>
|
||||
({ http: { getServerInfo: () => serverInfo } } as any);
|
||||
|
||||
describe('Reporting server createConfig$', () => {
|
||||
let mockCoreSetup: CoreSetup;
|
||||
let mockInitContext: PluginInitializerContext;
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeEach(() => {
|
||||
mockCoreSetup = makeMockCoreSetup({ host: 'kibanaHost', port: 5601, protocol: 'http' });
|
||||
mockInitContext = makeMockInitContext({
|
||||
kibanaServer: {},
|
||||
});
|
||||
mockLogger = ({ warn: jest.fn(), debug: jest.fn() } as unknown) as Logger;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('creates random encryption key and default config using host, protocol, and port from server info', async () => {
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.encryptionKey).toMatch(/\S{32,}/); // random 32 characters
|
||||
expect(result.kibanaServer).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"hostname": "kibanaHost",
|
||||
"port": 5601,
|
||||
"protocol": "http",
|
||||
}
|
||||
`);
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(1);
|
||||
expect((mockLogger.warn as any).mock.calls[0]).toMatchObject([
|
||||
'Generating a random key for xpack.reporting.encryptionKey. To prevent sessions from being invalidated on restart, please set xpack.reporting.encryptionKey in kibana.yml',
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses the user-provided encryption key', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii',
|
||||
kibanaServer: {},
|
||||
});
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.encryptionKey).toMatch('iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii');
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('uses the user-provided encryption key, reporting kibanaServer settings to override server info', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii',
|
||||
kibanaServer: {
|
||||
hostname: 'reportingHost',
|
||||
port: 5677,
|
||||
protocol: 'httpsa',
|
||||
},
|
||||
});
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"capture": Object {
|
||||
"browser": Object {
|
||||
"chromium": Object {
|
||||
"disableSandbox": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"encryptionKey": "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii",
|
||||
"kibanaServer": Object {
|
||||
"hostname": "reportingHost",
|
||||
"port": 5677,
|
||||
"protocol": "httpsa",
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('show warning when kibanaServer.hostName === "0"', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: 'aaaaaaaaaaaaabbbbbbbbbbbbaaaaaaaaa',
|
||||
kibanaServer: { hostname: '0' },
|
||||
});
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.kibanaServer).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"hostname": "0.0.0.0",
|
||||
"port": 5601,
|
||||
"protocol": "http",
|
||||
}
|
||||
`);
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(1);
|
||||
expect((mockLogger.warn as any).mock.calls[0]).toMatchObject([
|
||||
`Found 'server.host: \"0\"' in Kibana configuration. This is incompatible with Reporting. To enable Reporting to work, 'xpack.reporting.kibanaServer.hostname: 0.0.0.0' is being automatically ` +
|
||||
`to the configuration. You can change the setting to 'server.host: 0.0.0.0' or add 'xpack.reporting.kibanaServer.hostname: 0.0.0.0' in kibana.yml to prevent this message.`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('uses user-provided disableSandbox: false', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: '888888888888888888888888888888888',
|
||||
capture: { browser: { chromium: { disableSandbox: false } } },
|
||||
} as ReportingConfigType);
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.capture.browser.chromium).toMatchObject({ disableSandbox: false });
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('uses user-provided disableSandbox: true', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: '888888888888888888888888888888888',
|
||||
capture: { browser: { chromium: { disableSandbox: true } } },
|
||||
} as ReportingConfigType);
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.capture.browser.chromium).toMatchObject({ disableSandbox: true });
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(0);
|
||||
});
|
||||
|
||||
it('provides a default for disableSandbox', async () => {
|
||||
mockInitContext = makeMockInitContext({
|
||||
encryptionKey: '888888888888888888888888888888888',
|
||||
} as ReportingConfigType);
|
||||
const result = await createConfig$(mockCoreSetup, mockInitContext, mockLogger).toPromise();
|
||||
|
||||
expect(result.capture.browser.chromium).toMatchObject({ disableSandbox: expect.any(Boolean) });
|
||||
expect((mockLogger.warn as any).mock.calls.length).toBe(0);
|
||||
});
|
||||
});
|
124
x-pack/plugins/reporting/server/config/create_config.ts
Normal file
124
x-pack/plugins/reporting/server/config/create_config.ts
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n/';
|
||||
import { TypeOf } from '@kbn/config-schema';
|
||||
import crypto from 'crypto';
|
||||
import { capitalize } from 'lodash';
|
||||
import { map, mergeMap } from 'rxjs/operators';
|
||||
import { CoreSetup, Logger, PluginInitializerContext } from 'src/core/server';
|
||||
import { getDefaultChromiumSandboxDisabled } from './default_chromium_sandbox_disabled';
|
||||
import { ConfigSchema } from './schema';
|
||||
|
||||
/*
|
||||
* Set up dynamic config defaults
|
||||
* - xpack.capture.browser.chromium.disableSandbox
|
||||
* - xpack.kibanaServer
|
||||
* - xpack.reporting.encryptionKey
|
||||
*/
|
||||
export function createConfig$(core: CoreSetup, context: PluginInitializerContext, logger: Logger) {
|
||||
return context.config.create<TypeOf<typeof ConfigSchema>>().pipe(
|
||||
map(config => {
|
||||
// encryption key
|
||||
let encryptionKey = config.encryptionKey;
|
||||
if (encryptionKey === undefined) {
|
||||
logger.warn(
|
||||
i18n.translate('xpack.reporting.serverConfig.randomEncryptionKey', {
|
||||
defaultMessage:
|
||||
'Generating a random key for xpack.reporting.encryptionKey. To prevent sessions from being invalidated on ' +
|
||||
'restart, please set xpack.reporting.encryptionKey in kibana.yml',
|
||||
})
|
||||
);
|
||||
encryptionKey = crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
const { kibanaServer: reportingServer } = config;
|
||||
const serverInfo = core.http.getServerInfo();
|
||||
// kibanaServer.hostname, default to server.host, don't allow "0"
|
||||
let kibanaServerHostname = reportingServer.hostname
|
||||
? reportingServer.hostname
|
||||
: serverInfo.host;
|
||||
if (kibanaServerHostname === '0') {
|
||||
logger.warn(
|
||||
i18n.translate('xpack.reporting.serverConfig.invalidServerHostname', {
|
||||
defaultMessage:
|
||||
`Found 'server.host: "0"' in Kibana configuration. This is incompatible with Reporting. ` +
|
||||
`To enable Reporting to work, '{configKey}: 0.0.0.0' is being automatically to the configuration. ` +
|
||||
`You can change the setting to 'server.host: 0.0.0.0' or add '{configKey}: 0.0.0.0' in kibana.yml to prevent this message.`,
|
||||
values: { configKey: 'xpack.reporting.kibanaServer.hostname' },
|
||||
})
|
||||
);
|
||||
kibanaServerHostname = '0.0.0.0';
|
||||
}
|
||||
// kibanaServer.port, default to server.port
|
||||
const kibanaServerPort = reportingServer.port
|
||||
? reportingServer.port
|
||||
: serverInfo.port; // prettier-ignore
|
||||
// kibanaServer.protocol, default to server.protocol
|
||||
const kibanaServerProtocol = reportingServer.protocol
|
||||
? reportingServer.protocol
|
||||
: serverInfo.protocol;
|
||||
return {
|
||||
...config,
|
||||
encryptionKey,
|
||||
kibanaServer: {
|
||||
hostname: kibanaServerHostname,
|
||||
port: kibanaServerPort,
|
||||
protocol: kibanaServerProtocol,
|
||||
},
|
||||
};
|
||||
}),
|
||||
mergeMap(async config => {
|
||||
if (config.capture.browser.chromium.disableSandbox != null) {
|
||||
// disableSandbox was set by user
|
||||
return config;
|
||||
}
|
||||
|
||||
// disableSandbox was not set by user, apply default for OS
|
||||
const { os, disableSandbox } = await getDefaultChromiumSandboxDisabled();
|
||||
const osName = [os.os, os.dist, os.release]
|
||||
.filter(Boolean)
|
||||
.map(capitalize)
|
||||
.join(' ');
|
||||
|
||||
logger.debug(
|
||||
i18n.translate('xpack.reporting.serverConfig.osDetected', {
|
||||
defaultMessage: `Running on OS: '{osName}'`,
|
||||
values: { osName },
|
||||
})
|
||||
);
|
||||
|
||||
if (disableSandbox === true) {
|
||||
logger.warn(
|
||||
i18n.translate('xpack.reporting.serverConfig.autoSet.sandboxDisabled', {
|
||||
defaultMessage: `Chromium sandbox provides an additional layer of protection, but is not supported for {osName} OS. Automatically setting '{configKey}: true'.`,
|
||||
values: {
|
||||
configKey: 'xpack.reporting.capture.browser.chromium.disableSandbox',
|
||||
osName,
|
||||
},
|
||||
})
|
||||
);
|
||||
} else {
|
||||
logger.info(
|
||||
i18n.translate('xpack.reporting.serverConfig.autoSet.sandboxEnabled', {
|
||||
defaultMessage: `Chromium sandbox provides an additional layer of protection, and is supported for {osName} OS. Automatically enabling Chromium sandbox.`,
|
||||
values: { osName },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
capture: {
|
||||
...config.capture,
|
||||
browser: {
|
||||
...config.capture.browser,
|
||||
chromium: { ...config.capture.browser.chromium, disableSandbox },
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
|
@ -11,11 +11,17 @@ jest.mock('getos', () => {
|
|||
import { getDefaultChromiumSandboxDisabled } from './default_chromium_sandbox_disabled';
|
||||
import getos from 'getos';
|
||||
|
||||
function defaultTest(os, expectedDefault) {
|
||||
interface TestObject {
|
||||
os: string;
|
||||
dist?: string;
|
||||
release?: string;
|
||||
}
|
||||
|
||||
function defaultTest(os: TestObject, expectedDefault: boolean) {
|
||||
test(`${expectedDefault ? 'disabled' : 'enabled'} on ${JSON.stringify(os)}`, async () => {
|
||||
getos.mockImplementation(cb => cb(null, os));
|
||||
(getos as jest.Mock).mockImplementation(cb => cb(null, os));
|
||||
const actualDefault = await getDefaultChromiumSandboxDisabled();
|
||||
expect(actualDefault).toBe(expectedDefault);
|
||||
expect(actualDefault.disableSandbox).toBe(expectedDefault);
|
||||
});
|
||||
}
|
||||
|
|
@ -31,12 +31,17 @@ const distroSupportsUnprivilegedUsernamespaces = (distro: string) => {
|
|||
return true;
|
||||
};
|
||||
|
||||
export async function getDefaultChromiumSandboxDisabled() {
|
||||
interface OsSummary {
|
||||
disableSandbox: boolean;
|
||||
os: { os: string; dist?: string; release?: string };
|
||||
}
|
||||
|
||||
export async function getDefaultChromiumSandboxDisabled(): Promise<OsSummary> {
|
||||
const os = await getos();
|
||||
|
||||
if (os.os === 'linux' && !distroSupportsUnprivilegedUsernamespaces(os.dist)) {
|
||||
return true;
|
||||
return { os, disableSandbox: true };
|
||||
} else {
|
||||
return false;
|
||||
return { os, disableSandbox: false };
|
||||
}
|
||||
}
|
23
x-pack/plugins/reporting/server/config/index.ts
Normal file
23
x-pack/plugins/reporting/server/config/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 { PluginConfigDescriptor } from 'kibana/server';
|
||||
import { ConfigSchema, ConfigType } from './schema';
|
||||
|
||||
export { createConfig$ } from './create_config';
|
||||
|
||||
export const config: PluginConfigDescriptor<ConfigType> = {
|
||||
schema: ConfigSchema,
|
||||
deprecations: ({ unused }) => [
|
||||
unused('capture.browser.chromium.maxScreenshotDimension'),
|
||||
unused('capture.concurrency'),
|
||||
unused('capture.settleTime'),
|
||||
unused('capture.timeout'),
|
||||
unused('kibanaApp'),
|
||||
],
|
||||
};
|
||||
|
||||
export { ConfigSchema, ConfigType };
|
134
x-pack/plugins/reporting/server/config/schema.test.ts
Normal file
134
x-pack/plugins/reporting/server/config/schema.test.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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 { ConfigSchema } from './schema';
|
||||
|
||||
describe('Reporting Config Schema', () => {
|
||||
it(`context {"dev":false,"dist":false} produces correct config`, () => {
|
||||
expect(ConfigSchema.validate({}, { dev: false, dist: false })).toMatchObject({
|
||||
capture: {
|
||||
browser: {
|
||||
autoDownload: true,
|
||||
chromium: { proxy: { enabled: false } },
|
||||
type: 'chromium',
|
||||
},
|
||||
loadDelay: 3000,
|
||||
maxAttempts: 1,
|
||||
networkPolicy: {
|
||||
enabled: true,
|
||||
rules: [
|
||||
{ allow: true, host: undefined, protocol: 'http:' },
|
||||
{ allow: true, host: undefined, protocol: 'https:' },
|
||||
{ allow: true, host: undefined, protocol: 'ws:' },
|
||||
{ allow: true, host: undefined, protocol: 'wss:' },
|
||||
{ allow: true, host: undefined, protocol: 'data:' },
|
||||
{ allow: false, host: undefined, protocol: undefined },
|
||||
],
|
||||
},
|
||||
viewport: { height: 1200, width: 1950 },
|
||||
zoom: 2,
|
||||
},
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
enablePanelActionDownload: true,
|
||||
maxSizeBytes: 10485760,
|
||||
scroll: { duration: '30s', size: 500 },
|
||||
},
|
||||
encryptionKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
index: '.reporting',
|
||||
kibanaServer: {},
|
||||
poll: {
|
||||
jobCompletionNotifier: { interval: 10000, intervalErrorMultiplier: 5 },
|
||||
jobsRefresh: { interval: 5000, intervalErrorMultiplier: 5 },
|
||||
},
|
||||
queue: {
|
||||
indexInterval: 'week',
|
||||
pollEnabled: true,
|
||||
pollInterval: 3000,
|
||||
pollIntervalErrorMultiplier: 10,
|
||||
timeout: 120000,
|
||||
},
|
||||
roles: { allow: ['reporting_user'] },
|
||||
});
|
||||
});
|
||||
|
||||
it(`context {"dev":false,"dist":true} produces correct config`, () => {
|
||||
expect(ConfigSchema.validate({}, { dev: false, dist: true })).toMatchObject({
|
||||
capture: {
|
||||
browser: {
|
||||
autoDownload: false,
|
||||
chromium: {
|
||||
inspect: false,
|
||||
proxy: { enabled: false },
|
||||
},
|
||||
type: 'chromium',
|
||||
},
|
||||
loadDelay: 3000,
|
||||
maxAttempts: 3,
|
||||
networkPolicy: {
|
||||
enabled: true,
|
||||
rules: [
|
||||
{ allow: true, host: undefined, protocol: 'http:' },
|
||||
{ allow: true, host: undefined, protocol: 'https:' },
|
||||
{ allow: true, host: undefined, protocol: 'ws:' },
|
||||
{ allow: true, host: undefined, protocol: 'wss:' },
|
||||
{ allow: true, host: undefined, protocol: 'data:' },
|
||||
{ allow: false, host: undefined, protocol: undefined },
|
||||
],
|
||||
},
|
||||
viewport: { height: 1200, width: 1950 },
|
||||
zoom: 2,
|
||||
},
|
||||
csv: {
|
||||
checkForFormulas: true,
|
||||
enablePanelActionDownload: true,
|
||||
maxSizeBytes: 10485760,
|
||||
scroll: { duration: '30s', size: 500 },
|
||||
},
|
||||
index: '.reporting',
|
||||
kibanaServer: {},
|
||||
poll: {
|
||||
jobCompletionNotifier: { interval: 10000, intervalErrorMultiplier: 5 },
|
||||
jobsRefresh: { interval: 5000, intervalErrorMultiplier: 5 },
|
||||
},
|
||||
queue: {
|
||||
indexInterval: 'week',
|
||||
pollEnabled: true,
|
||||
pollInterval: 3000,
|
||||
pollIntervalErrorMultiplier: 10,
|
||||
timeout: 120000,
|
||||
},
|
||||
roles: { allow: ['reporting_user'] },
|
||||
});
|
||||
});
|
||||
|
||||
it(`allows optional settings`, () => {
|
||||
// encryption key
|
||||
expect(
|
||||
ConfigSchema.validate({ encryptionKey: 'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq' })
|
||||
.encryptionKey
|
||||
).toBe('qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq');
|
||||
|
||||
// disableSandbox
|
||||
expect(
|
||||
ConfigSchema.validate({ capture: { browser: { chromium: { disableSandbox: true } } } })
|
||||
.capture.browser.chromium
|
||||
).toMatchObject({ disableSandbox: true, proxy: { enabled: false } });
|
||||
|
||||
// kibanaServer
|
||||
expect(
|
||||
ConfigSchema.validate({ kibanaServer: { hostname: 'Frodo' } }).kibanaServer
|
||||
).toMatchObject({ hostname: 'Frodo' });
|
||||
});
|
||||
|
||||
it(`logs the proper validation messages`, () => {
|
||||
// kibanaServer
|
||||
const throwValidationErr = () => ConfigSchema.validate({ kibanaServer: { hostname: '0' } });
|
||||
expect(throwValidationErr).toThrowError(
|
||||
`[kibanaServer.hostname]: must not be "0" for the headless browser to correctly resolve the host`
|
||||
);
|
||||
});
|
||||
});
|
174
x-pack/plugins/reporting/server/config/schema.ts
Normal file
174
x-pack/plugins/reporting/server/config/schema.ts
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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 { schema, TypeOf } from '@kbn/config-schema';
|
||||
import moment from 'moment';
|
||||
|
||||
const KibanaServerSchema = schema.object({
|
||||
hostname: schema.maybe(
|
||||
schema.string({
|
||||
validate(value) {
|
||||
if (value === '0') {
|
||||
return 'must not be "0" for the headless browser to correctly resolve the host';
|
||||
}
|
||||
},
|
||||
hostname: true,
|
||||
})
|
||||
),
|
||||
port: schema.maybe(schema.number()),
|
||||
protocol: schema.maybe(
|
||||
schema.string({
|
||||
validate(value) {
|
||||
if (!/^https?$/.test(value)) {
|
||||
return 'must be "http" or "https"';
|
||||
}
|
||||
},
|
||||
})
|
||||
),
|
||||
}); // default values are all dynamic in createConfig$
|
||||
|
||||
const QueueSchema = schema.object({
|
||||
indexInterval: schema.string({ defaultValue: 'week' }),
|
||||
pollEnabled: schema.boolean({ defaultValue: true }),
|
||||
pollInterval: schema.number({ defaultValue: 3000 }),
|
||||
pollIntervalErrorMultiplier: schema.number({ defaultValue: 10 }),
|
||||
timeout: schema.number({ defaultValue: moment.duration(2, 'm').asMilliseconds() }),
|
||||
});
|
||||
|
||||
const RulesSchema = schema.object({
|
||||
allow: schema.boolean(),
|
||||
host: schema.maybe(schema.string()),
|
||||
protocol: schema.maybe(schema.string()),
|
||||
});
|
||||
|
||||
const CaptureSchema = schema.object({
|
||||
timeouts: schema.object({
|
||||
openUrl: schema.number({ defaultValue: 30000 }),
|
||||
waitForElements: schema.number({ defaultValue: 30000 }),
|
||||
renderComplete: schema.number({ defaultValue: 30000 }),
|
||||
}),
|
||||
networkPolicy: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
rules: schema.arrayOf(RulesSchema, {
|
||||
defaultValue: [
|
||||
{ host: undefined, allow: true, protocol: 'http:' },
|
||||
{ host: undefined, allow: true, protocol: 'https:' },
|
||||
{ host: undefined, allow: true, protocol: 'ws:' },
|
||||
{ host: undefined, allow: true, protocol: 'wss:' },
|
||||
{ host: undefined, allow: true, protocol: 'data:' },
|
||||
{ host: undefined, allow: false, protocol: undefined }, // Default action is to deny!
|
||||
],
|
||||
}),
|
||||
}),
|
||||
zoom: schema.number({ defaultValue: 2 }),
|
||||
viewport: schema.object({
|
||||
width: schema.number({ defaultValue: 1950 }),
|
||||
height: schema.number({ defaultValue: 1200 }),
|
||||
}),
|
||||
loadDelay: schema.number({
|
||||
defaultValue: moment.duration(3, 's').asMilliseconds(),
|
||||
}), // TODO: use schema.duration
|
||||
browser: schema.object({
|
||||
autoDownload: schema.conditional(
|
||||
schema.contextRef('dist'),
|
||||
true,
|
||||
schema.boolean({ defaultValue: false }),
|
||||
schema.boolean({ defaultValue: true })
|
||||
),
|
||||
chromium: schema.object({
|
||||
inspect: schema.conditional(
|
||||
schema.contextRef('dist'),
|
||||
true,
|
||||
schema.boolean({ defaultValue: false }),
|
||||
schema.maybe(schema.never())
|
||||
),
|
||||
disableSandbox: schema.maybe(schema.boolean()), // default value is dynamic in createConfig$
|
||||
proxy: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
server: schema.conditional(
|
||||
schema.siblingRef('enabled'),
|
||||
true,
|
||||
schema.uri({ scheme: ['http', 'https'] }),
|
||||
schema.maybe(schema.never())
|
||||
),
|
||||
bypass: schema.conditional(
|
||||
schema.siblingRef('enabled'),
|
||||
true,
|
||||
schema.arrayOf(schema.string({ hostname: true })),
|
||||
schema.maybe(schema.never())
|
||||
),
|
||||
}),
|
||||
}),
|
||||
type: schema.string({ defaultValue: 'chromium' }),
|
||||
}),
|
||||
maxAttempts: schema.conditional(
|
||||
schema.contextRef('dist'),
|
||||
true,
|
||||
schema.number({ defaultValue: 3 }),
|
||||
schema.number({ defaultValue: 1 })
|
||||
),
|
||||
});
|
||||
|
||||
const CsvSchema = schema.object({
|
||||
checkForFormulas: schema.boolean({ defaultValue: true }),
|
||||
enablePanelActionDownload: schema.boolean({ defaultValue: true }),
|
||||
maxSizeBytes: schema.number({
|
||||
defaultValue: 1024 * 1024 * 10, // 10MB
|
||||
}), // TODO: use schema.byteSize
|
||||
useByteOrderMarkEncoding: schema.boolean({ defaultValue: false }),
|
||||
scroll: schema.object({
|
||||
duration: schema.string({
|
||||
defaultValue: '30s',
|
||||
validate(value) {
|
||||
if (!/^[0-9]+(d|h|m|s|ms|micros|nanos)$/.test(value)) {
|
||||
return 'must be a duration string';
|
||||
}
|
||||
},
|
||||
}),
|
||||
size: schema.number({ defaultValue: 500 }),
|
||||
}),
|
||||
});
|
||||
|
||||
const EncryptionKeySchema = schema.conditional(
|
||||
schema.contextRef('dist'),
|
||||
true,
|
||||
schema.maybe(schema.string({ minLength: 32 })), // default value is dynamic in createConfig$
|
||||
schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) })
|
||||
);
|
||||
|
||||
const RolesSchema = schema.object({
|
||||
allow: schema.arrayOf(schema.string(), { defaultValue: ['reporting_user'] }),
|
||||
});
|
||||
|
||||
const IndexSchema = schema.string({ defaultValue: '.reporting' });
|
||||
|
||||
const PollSchema = schema.object({
|
||||
jobCompletionNotifier: schema.object({
|
||||
interval: schema.number({
|
||||
defaultValue: moment.duration(10, 's').asMilliseconds(),
|
||||
}), // TODO: use schema.duration
|
||||
intervalErrorMultiplier: schema.number({ defaultValue: 5 }),
|
||||
}),
|
||||
jobsRefresh: schema.object({
|
||||
interval: schema.number({
|
||||
defaultValue: moment.duration(5, 's').asMilliseconds(),
|
||||
}), // TODO: use schema.duration
|
||||
intervalErrorMultiplier: schema.number({ defaultValue: 5 }),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ConfigSchema = schema.object({
|
||||
kibanaServer: KibanaServerSchema,
|
||||
queue: QueueSchema,
|
||||
capture: CaptureSchema,
|
||||
csv: CsvSchema,
|
||||
encryptionKey: EncryptionKeySchema,
|
||||
roles: RolesSchema,
|
||||
index: IndexSchema,
|
||||
poll: PollSchema,
|
||||
});
|
||||
|
||||
export type ConfigType = TypeOf<typeof ConfigSchema>;
|
14
x-pack/plugins/reporting/server/index.ts
Normal file
14
x-pack/plugins/reporting/server/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 { PluginInitializerContext } from 'src/core/server';
|
||||
import { ReportingPlugin } from './plugin';
|
||||
|
||||
export { config, ConfigSchema } from './config';
|
||||
export { ConfigType, PluginsSetup } from './plugin';
|
||||
|
||||
export const plugin = (initializerContext: PluginInitializerContext) =>
|
||||
new ReportingPlugin(initializerContext);
|
38
x-pack/plugins/reporting/server/plugin.ts
Normal file
38
x-pack/plugins/reporting/server/plugin.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 { Observable } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/server';
|
||||
import { ConfigType, createConfig$ } from './config';
|
||||
|
||||
export interface PluginsSetup {
|
||||
/** @deprecated */
|
||||
__legacy: {
|
||||
config$: Observable<ConfigType>;
|
||||
};
|
||||
}
|
||||
|
||||
export class ReportingPlugin implements Plugin<PluginsSetup> {
|
||||
private readonly log: Logger;
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.log = this.initializerContext.logger.get();
|
||||
}
|
||||
|
||||
public async setup(core: CoreSetup): Promise<PluginsSetup> {
|
||||
return {
|
||||
__legacy: {
|
||||
config$: createConfig$(core, this.initializerContext, this.log).pipe(first()),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public start() {}
|
||||
public stop() {}
|
||||
}
|
||||
|
||||
export { ConfigType };
|
|
@ -12431,7 +12431,6 @@
|
|||
"xpack.reporting.screenCapturePanelContent.optimizeForPrintingLabel": "印刷用に最適化",
|
||||
"xpack.reporting.selfCheck.ok": "レポートプラグイン自己チェックOK!",
|
||||
"xpack.reporting.selfCheck.warning": "レポートプラグイン自己チェックで警告が発生しました: {err}",
|
||||
"xpack.reporting.selfCheckEncryptionKey.warning": "{setting}のランダムキーを生成しています。保留中のレポートの再開が失敗しないように、kibana.ymlで{setting}を設定してください",
|
||||
"xpack.reporting.shareContextMenu.csvReportsButtonLabel": "CSV レポート",
|
||||
"xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF レポート",
|
||||
"xpack.reporting.shareContextMenu.pngReportsButtonLabel": "PNG レポート",
|
||||
|
|
|
@ -12435,7 +12435,6 @@
|
|||
"xpack.reporting.screenCapturePanelContent.optimizeForPrintingLabel": "打印优化",
|
||||
"xpack.reporting.selfCheck.ok": "Reporting 插件自检正常!",
|
||||
"xpack.reporting.selfCheck.warning": "Reporting 插件自检生成警告:{err}",
|
||||
"xpack.reporting.selfCheckEncryptionKey.warning": "正在为 {setting} 生成随机密钥。要防止待处理报告在重新启动时失败,请在 kibana.yml 中设置 {setting}",
|
||||
"xpack.reporting.shareContextMenu.csvReportsButtonLabel": "CSV 报告",
|
||||
"xpack.reporting.shareContextMenu.pdfReportsButtonLabel": "PDF 报告",
|
||||
"xpack.reporting.shareContextMenu.pngReportsButtonLabel": "PNG 报告",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue