mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* Addresses an issue where Chromium user-data-dirs aren't removed (#40284) * Addresses an issue where Chromium user-data-dirs aren't cleaned properly, and moves to TS * Fixing renovate config issues * Fixing issues with typedef's missing * Using prior-set typedefs for logger * Fixing lint issues * Lint fixes * No module def
This commit is contained in:
parent
e12f403ab6
commit
894556c5dd
12 changed files with 117 additions and 95 deletions
|
@ -320,7 +320,6 @@
|
|||
"@types/opn": "^5.1.0",
|
||||
"@types/podium": "^1.0.0",
|
||||
"@types/prop-types": "^15.5.3",
|
||||
"@types/puppeteer-core": "^1.9.0",
|
||||
"@types/react": "^16.8.0",
|
||||
"@types/react-dom": "^16.8.0",
|
||||
"@types/react-redux": "^6.0.6",
|
||||
|
|
|
@ -42,7 +42,7 @@ function generatePngObservableFn(server) {
|
|||
throw new Error(`LayoutParams.Dimensions is undefined.`);
|
||||
}
|
||||
|
||||
const layout = new PreserveLayout(layoutParams.dimensions);
|
||||
const layout = new PreserveLayout(layoutParams.dimensions);
|
||||
|
||||
const screenshots$ = urlScreenshotsObservable(url, conditionalHeaders, layout, browserTimezone);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
// @ts-ignore no module definition
|
||||
import * as Chrome from 'puppeteer-core';
|
||||
import { parse as parseUrl } from 'url';
|
||||
import {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
interface Opts {
|
||||
export interface IArgOptions {
|
||||
userDataDir: string;
|
||||
viewport: { width: number; height: number };
|
||||
disableSandbox: boolean;
|
||||
|
@ -13,7 +13,7 @@ interface Opts {
|
|||
server: string;
|
||||
bypass?: string[];
|
||||
};
|
||||
verboseLogging: boolean;
|
||||
verboseLogging?: boolean;
|
||||
}
|
||||
|
||||
export const args = ({
|
||||
|
@ -22,7 +22,7 @@ export const args = ({
|
|||
disableSandbox,
|
||||
proxyConfig,
|
||||
verboseLogging,
|
||||
}: Opts) => {
|
||||
}: IArgOptions) => {
|
||||
const flags = [
|
||||
// Disable built-in Google Translate service
|
||||
'--disable-translate',
|
||||
|
|
|
@ -6,21 +6,41 @@
|
|||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
// @ts-ignore
|
||||
import puppeteer from 'puppeteer-core';
|
||||
import rimraf from 'rimraf';
|
||||
import * as Rx from 'rxjs';
|
||||
import { map, share, mergeMap, filter, partition } from 'rxjs/operators';
|
||||
import { InnerSubscriber } from 'rxjs/internal/InnerSubscriber';
|
||||
|
||||
import { HeadlessChromiumDriver } from '../driver';
|
||||
import { args } from './args';
|
||||
import { args, IArgOptions } from './args';
|
||||
import { safeChildProcess } from '../../safe_child_process';
|
||||
import { getChromeLogLocation } from '../paths';
|
||||
import { Logger } from '../../../../types';
|
||||
|
||||
const compactWhitespace = str => {
|
||||
type binaryPath = string;
|
||||
type queueTimeout = number;
|
||||
interface IBrowserConfig {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const compactWhitespace = (str: string) => {
|
||||
return str.replace(/\s+/, ' ');
|
||||
};
|
||||
|
||||
export class HeadlessChromiumDriverFactory {
|
||||
constructor(binaryPath, logger, browserConfig, queueTimeout) {
|
||||
private binaryPath: binaryPath;
|
||||
private logger: Logger;
|
||||
private browserConfig: IBrowserConfig;
|
||||
private queueTimeout: queueTimeout;
|
||||
|
||||
constructor(
|
||||
binaryPath: binaryPath,
|
||||
logger: any,
|
||||
browserConfig: IBrowserConfig,
|
||||
queueTimeout: queueTimeout
|
||||
) {
|
||||
this.binaryPath = binaryPath;
|
||||
this.logger = logger.clone(['chromium-driver-factory']);
|
||||
this.browserConfig = browserConfig;
|
||||
|
@ -29,7 +49,10 @@ export class HeadlessChromiumDriverFactory {
|
|||
|
||||
type = 'chromium';
|
||||
|
||||
test({ viewport, browserTimezone }, logger) {
|
||||
test(
|
||||
{ viewport, browserTimezone }: { viewport: IArgOptions['viewport']; browserTimezone: string },
|
||||
logger: any
|
||||
) {
|
||||
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-'));
|
||||
const chromiumArgs = args({
|
||||
userDataDir,
|
||||
|
@ -49,7 +72,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
TZ: browserTimezone,
|
||||
},
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error: Error) => {
|
||||
logger.warning(
|
||||
`The Reporting plugin encountered issues launching Chromium in a self-test. You may have trouble generating reports: [${error}]`
|
||||
);
|
||||
|
@ -58,8 +81,14 @@ export class HeadlessChromiumDriverFactory {
|
|||
});
|
||||
}
|
||||
|
||||
create({ viewport, browserTimezone }) {
|
||||
return Rx.Observable.create(async observer => {
|
||||
create({
|
||||
viewport,
|
||||
browserTimezone,
|
||||
}: {
|
||||
viewport: IArgOptions['viewport'];
|
||||
browserTimezone: string;
|
||||
}): Rx.Observable<any> {
|
||||
return Rx.Observable.create(async (observer: InnerSubscriber<any, any>) => {
|
||||
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'chromium-'));
|
||||
const chromiumArgs = args({
|
||||
userDataDir,
|
||||
|
@ -69,8 +98,8 @@ export class HeadlessChromiumDriverFactory {
|
|||
proxyConfig: this.browserConfig.proxy,
|
||||
});
|
||||
|
||||
let browser;
|
||||
let page;
|
||||
let browser: puppeteer.Browser;
|
||||
let page: puppeteer.Page;
|
||||
try {
|
||||
browser = await puppeteer.launch({
|
||||
userDataDir,
|
||||
|
@ -88,6 +117,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
// which can cause the job to fail even if we bump timeouts in
|
||||
// the config. Help alleviate errors like
|
||||
// "TimeoutError: waiting for selector ".application" failed: timeout 30000ms exceeded"
|
||||
// @ts-ignore outdated typedefs for puppteer
|
||||
page.setDefaultTimeout(this.queueTimeout);
|
||||
} catch (err) {
|
||||
observer.error(new Error(`Error spawning Chromium browser: [${err}]`));
|
||||
|
@ -107,19 +137,18 @@ export class HeadlessChromiumDriverFactory {
|
|||
// https://pptr.dev/#?product=Puppeteer&version=v1.10.0&show=api-event-error
|
||||
// https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page
|
||||
|
||||
const stderr$ = Rx.fromEvent(page, 'console').pipe(
|
||||
filter(line => line._type === 'error'),
|
||||
map(line => line._text),
|
||||
const stderr$ = Rx.fromEvent(page as NodeJS.EventEmitter, 'console').pipe(
|
||||
filter((line: any) => line._type === 'error'),
|
||||
map((line: any) => line._text),
|
||||
share()
|
||||
);
|
||||
|
||||
const [consoleMessage$, message$] = stderr$.pipe(
|
||||
partition(msg => msg.match(/\[\d+\/\d+.\d+:\w+:CONSOLE\(\d+\)\]/))
|
||||
);
|
||||
const [consoleMessage$, message$] = partition(
|
||||
(msg: string) => !!msg.match(/\[\d+\/\d+.\d+:\w+:CONSOLE\(\d+\)\]/)
|
||||
)(stderr$);
|
||||
|
||||
const driver$ = Rx.of(
|
||||
new HeadlessChromiumDriver(page, {
|
||||
maxScreenshotDimension: this.browserConfig.maxScreenshotDimension,
|
||||
logger: this.logger,
|
||||
})
|
||||
);
|
||||
|
@ -133,7 +162,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
);
|
||||
|
||||
const processRequestFailed$ = Rx.fromEvent(page, 'requestfailed').pipe(
|
||||
mergeMap(req => {
|
||||
mergeMap((req: any) => {
|
||||
const failure = req.failure && req.failure();
|
||||
if (failure) {
|
||||
return Rx.throwError(
|
||||
|
@ -151,12 +180,12 @@ export class HeadlessChromiumDriverFactory {
|
|||
);
|
||||
|
||||
const nssError$ = message$.pipe(
|
||||
filter(line => line.includes('error while loading shared libraries: libnss3.so')),
|
||||
filter((line: string) => line.includes('error while loading shared libraries: libnss3.so')),
|
||||
mergeMap(() => Rx.throwError(new Error(`You must install nss for Reporting to work`)))
|
||||
);
|
||||
|
||||
const fontError$ = message$.pipe(
|
||||
filter(line =>
|
||||
filter((line: string) =>
|
||||
line.includes('Check failed: InitDefaultFont(). Could not find the default font')
|
||||
),
|
||||
mergeMap(() =>
|
||||
|
@ -165,7 +194,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
);
|
||||
|
||||
const noUsableSandbox$ = message$.pipe(
|
||||
filter(line => line.includes('No usable sandbox! Update your kernel')),
|
||||
filter((line: string) => line.includes('No usable sandbox! Update your kernel')),
|
||||
mergeMap(() =>
|
||||
Rx.throwError(
|
||||
new Error(
|
||||
|
@ -196,7 +225,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
});
|
||||
|
||||
// unsubscribe logic makes a best-effort attempt to delete the user data directory used by chromium
|
||||
return () => {
|
||||
observer.add(() => {
|
||||
this.logger.debug(`deleting chromium user data directory at [${userDataDir}]`);
|
||||
// the unsubscribe function isn't `async` so we're going to make our best effort at
|
||||
// deleting the userDataDir and if it fails log an error.
|
||||
|
@ -207,7 +236,7 @@ export class HeadlessChromiumDriverFactory {
|
|||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 path from 'path';
|
||||
|
||||
export const paths = {
|
||||
archivesPath: path.resolve(__dirname, '../../../.chromium'),
|
||||
baseUrl: 'https://s3.amazonaws.com/headless-shell/',
|
||||
packages: [{
|
||||
platforms: ['darwin', 'freebsd', 'openbsd'],
|
||||
archiveFilename: 'chromium-2fac04a-darwin.zip',
|
||||
archiveChecksum: '36814b1629457aa178b4ecdf6cc1bc5f',
|
||||
rawChecksum: '9b40e2efa7f4f1870835ee4cdaf1dd51',
|
||||
binaryRelativePath: 'headless_shell-darwin/headless_shell',
|
||||
}, {
|
||||
platforms: ['linux'],
|
||||
archiveFilename: 'chromium-2fac04a-linux.zip',
|
||||
archiveChecksum: '5cd6b898a35f9dc0ba6f49d821b8a2a3',
|
||||
rawChecksum: 'b3fd218d3c3446c388da4e6c8a82754c',
|
||||
binaryRelativePath: 'headless_shell-linux/headless_shell'
|
||||
}, {
|
||||
platforms: ['win32'],
|
||||
archiveFilename: 'chromium-2fac04a-windows.zip',
|
||||
archiveChecksum: '1499a4d5847792d59b9c1a8ab7dc8b94',
|
||||
rawChecksum: '08b48d2f3d23c4bc8b58779ca4a7b627',
|
||||
binaryRelativePath: 'headless_shell-windows\\headless_shell.exe'
|
||||
}]
|
||||
};
|
||||
|
||||
export const getChromeLogLocation = (binaryPath) =>
|
||||
path.join(binaryPath, '..', 'chrome_debug.log');
|
|
@ -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 path from 'path';
|
||||
|
||||
export const paths = {
|
||||
archivesPath: path.resolve(__dirname, '../../../.chromium'),
|
||||
baseUrl: 'https://s3.amazonaws.com/headless-shell/',
|
||||
packages: [
|
||||
{
|
||||
platforms: ['darwin', 'freebsd', 'openbsd'],
|
||||
archiveFilename: 'chromium-2fac04a-darwin.zip',
|
||||
archiveChecksum: '36814b1629457aa178b4ecdf6cc1bc5f',
|
||||
rawChecksum: '9b40e2efa7f4f1870835ee4cdaf1dd51',
|
||||
binaryRelativePath: 'headless_shell-darwin/headless_shell',
|
||||
},
|
||||
{
|
||||
platforms: ['linux'],
|
||||
archiveFilename: 'chromium-2fac04a-linux.zip',
|
||||
archiveChecksum: '5cd6b898a35f9dc0ba6f49d821b8a2a3',
|
||||
rawChecksum: 'b3fd218d3c3446c388da4e6c8a82754c',
|
||||
binaryRelativePath: 'headless_shell-linux/headless_shell',
|
||||
},
|
||||
{
|
||||
platforms: ['win32'],
|
||||
archiveFilename: 'chromium-2fac04a-windows.zip',
|
||||
archiveChecksum: '1499a4d5847792d59b9c1a8ab7dc8b94',
|
||||
rawChecksum: '08b48d2f3d23c4bc8b58779ca4a7b627',
|
||||
binaryRelativePath: 'headless_shell-windows\\headless_shell.exe',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const getChromeLogLocation = (binaryPath: string) =>
|
||||
path.join(binaryPath, '..', 'chrome_debug.log');
|
|
@ -6,44 +6,44 @@
|
|||
|
||||
import * as Rx from 'rxjs';
|
||||
import { take, share, mapTo, delay, tap, ignoreElements } from 'rxjs/operators';
|
||||
import { InnerSubscriber } from 'rxjs/internal/InnerSubscriber';
|
||||
|
||||
interface IChild {
|
||||
kill: (signal: string) => Promise<any>;
|
||||
}
|
||||
|
||||
// Our process can get sent various signals, and when these occur we wish to
|
||||
// kill the subprocess and then kill our process as long as the observer isn't cancelled
|
||||
export function safeChildProcess(childProcess, observer) {
|
||||
export function safeChildProcess(childProcess: IChild, observer: InnerSubscriber<any, any>) {
|
||||
const ownTerminateSignal$ = Rx.merge(
|
||||
Rx.fromEvent(process, 'SIGTERM').pipe(mapTo('SIGTERM')),
|
||||
Rx.fromEvent(process, 'SIGINT').pipe(mapTo('SIGINT')),
|
||||
Rx.fromEvent(process, 'SIGBREAK').pipe(mapTo('SIGBREAK')),
|
||||
)
|
||||
.pipe(
|
||||
take(1),
|
||||
share()
|
||||
);
|
||||
Rx.fromEvent(process as NodeJS.EventEmitter, 'SIGTERM').pipe(mapTo('SIGTERM')),
|
||||
Rx.fromEvent(process as NodeJS.EventEmitter, 'SIGINT').pipe(mapTo('SIGINT')),
|
||||
Rx.fromEvent(process as NodeJS.EventEmitter, 'SIGBREAK').pipe(mapTo('SIGBREAK'))
|
||||
).pipe(
|
||||
take(1),
|
||||
share()
|
||||
);
|
||||
|
||||
// signals that will be sent to the child process as a result of the main process
|
||||
// being sent these signals, or the exit being triggered
|
||||
const signalForChildProcess$ = Rx.merge(
|
||||
// SIGKILL when this process gets a terminal signal
|
||||
ownTerminateSignal$.pipe(
|
||||
mapTo('SIGKILL')
|
||||
),
|
||||
ownTerminateSignal$.pipe(mapTo('SIGKILL')),
|
||||
|
||||
// SIGKILL when this process forcefully exits
|
||||
Rx.fromEvent(process, 'exit').pipe(
|
||||
Rx.fromEvent(process as NodeJS.EventEmitter, 'exit').pipe(
|
||||
take(1),
|
||||
mapTo('SIGKILL')
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// send termination signals
|
||||
const terminate$ = Rx.merge(
|
||||
signalForChildProcess$.pipe(
|
||||
tap(signal => childProcess.kill(signal))
|
||||
),
|
||||
signalForChildProcess$.pipe(tap(signal => childProcess.kill(signal))),
|
||||
|
||||
ownTerminateSignal$.pipe(
|
||||
delay(1),
|
||||
tap(signal => process.kill(process.pid, signal)),
|
||||
tap(signal => process.kill(process.pid, signal))
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -58,7 +58,7 @@ export function safeChildProcess(childProcess, observer) {
|
|||
|
||||
// If a process exits ungracefully, we can try to help the user make sense of why
|
||||
// by giving them a suggestion based on the code.
|
||||
export function exitCodeSuggestion(code) {
|
||||
export function exitCodeSuggestion(code: number | null) {
|
||||
if (code === null) {
|
||||
return 'Your report may be too large. Try removing some visualizations or increasing the RAM available to Kibana.';
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
// @ts-ignore no module definition
|
||||
import * as puppeteer from 'puppeteer-core';
|
||||
import { KbnServer, Logger } from '../../../types';
|
||||
import { CHROMIUM } from '../../browsers/browser_types';
|
||||
|
|
1
x-pack/legacy/plugins/reporting/types.d.ts
vendored
1
x-pack/legacy/plugins/reporting/types.d.ts
vendored
|
@ -60,6 +60,7 @@ export interface Logger {
|
|||
error: (message: string) => void;
|
||||
warning: (message: string) => void;
|
||||
clone?: (tags: string[]) => Logger;
|
||||
isVerbose?: boolean;
|
||||
}
|
||||
|
||||
export interface ViewZoomWidthHeight {
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
"@types/pngjs": "^3.3.1",
|
||||
"@types/prop-types": "^15.5.3",
|
||||
"@types/proper-lockfile": "^3.0.0",
|
||||
"@types/puppeteer": "^1.12.4",
|
||||
"@types/react": "^16.8.0",
|
||||
"@types/react-dom": "^16.8.0",
|
||||
"@types/react-redux": "^6.0.6",
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -4208,21 +4208,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/proper-lockfile/-/proper-lockfile-3.0.0.tgz#dcc7cc3714857a4ae6583331d2687e89dc5c94d2"
|
||||
integrity sha512-+tfnsA3KNPDm7Sj9x5omRgvS6ALc+edjTZXYeR3kVEm+qmsrF+59yJUWZDreV/O0+EQ6t0YSWlzxfdV58UOEVg==
|
||||
|
||||
"@types/puppeteer-core@^1.9.0":
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/puppeteer-core/-/puppeteer-core-1.9.0.tgz#5ceb397e3ff769081fb07d71289b5009392d24d3"
|
||||
integrity sha512-YJwGTq0a8xZxN7/QDeW59XMdKTRNzDTc8ZVBPDB6J13GgXn1+QzgMA8pAq1/bj2FD0R7xj3nYoZra10b0HLzFw==
|
||||
dependencies:
|
||||
"@types/puppeteer" "*"
|
||||
|
||||
"@types/puppeteer@*":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.11.1.tgz#f2fe2e08917af2b4dc4b03bd2b838c05cb9d8410"
|
||||
integrity sha512-IvrvZfWjITUH7q4WrM25ul9xlIeLM3Oh+hV2FL7xQQSroVf8mX3lMZaN7XEsw6Bdfp99Qm7I4GcD+ak5+wIEfA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/puppeteer@^1.6.0":
|
||||
"@types/puppeteer@^1.12.4", "@types/puppeteer@^1.6.0":
|
||||
version "1.12.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/puppeteer/-/puppeteer-1.12.4.tgz#8388efdb0b30a54a7e7c4831ca0d709191d77ff1"
|
||||
integrity sha512-aaGbJaJ9TuF9vZfTeoh876sBa+rYJWPwtsmHmYr28pGr42ewJnkDTq2aeSKEmS39SqUdkwLj73y/d7rBSp7mDQ==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue