mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Merge branch 'master' of github.com:elastic/kibana
This commit is contained in:
commit
1cf86bfadb
52 changed files with 917 additions and 619 deletions
|
@ -3,7 +3,11 @@
|
|||
== Kibana Dashboard Only Mode
|
||||
|
||||
If {security} is enabled, you can use the `kibana_dashboard_only_user` built-in role to limit
|
||||
what users see when they log in to {kib}.
|
||||
what users see when they log in to {kib}. The `kibana_dashboard_only_user` role is
|
||||
preconfigured with read-only permissions to {kib}.
|
||||
|
||||
IMPORTANT: You must also assign roles that grant the user appropriate access to the data indices.
|
||||
For information on roles and privileges, see {xpack-ref}/authorization.html[User Authorization].
|
||||
|
||||
Users assigned this role are only able to see the Dashboard app in the navigation
|
||||
pane. When users open a dashboard, they will have a limited visual experience.
|
||||
|
@ -13,12 +17,7 @@ All edit and create controls are hidden.
|
|||
image:management/dashboard_only_mode/images/view_only_dashboard.png["View Only Dashboard"]
|
||||
|
||||
To assign this role, go to *Management > Security > Users*, add or edit
|
||||
a user, and add the `kibana_dashboard_only_user` role. You must assign roles
|
||||
that grant the user appropriate data access. For information on roles
|
||||
and privileges, see {xpack-ref}/authorization.html[User Authorization].
|
||||
|
||||
The `kibana_dashboard_only_user` role is
|
||||
preconfigured with read-only permissions to {kib}.
|
||||
a user, and add the `kibana_dashboard_only_user` role.
|
||||
|
||||
IMPORTANT: If you assign users the `kibana_dashboard_only_user` role, along with a role
|
||||
with write permissions to {kib}, they *will* have write access,
|
||||
|
|
|
@ -21,33 +21,14 @@
|
|||
|
||||
import { EnvOptions } from '../../env';
|
||||
|
||||
interface MockEnvOptions {
|
||||
config?: string;
|
||||
kbnServer?: any;
|
||||
mode?: EnvOptions['mode']['name'];
|
||||
packageInfo?: Partial<EnvOptions['packageInfo']>;
|
||||
}
|
||||
|
||||
export function getEnvOptions({
|
||||
config,
|
||||
kbnServer,
|
||||
mode = 'development',
|
||||
packageInfo = {},
|
||||
}: MockEnvOptions = {}): EnvOptions {
|
||||
export function getEnvOptions(options: Partial<EnvOptions> = {}): EnvOptions {
|
||||
return {
|
||||
config,
|
||||
kbnServer,
|
||||
mode: {
|
||||
dev: mode === 'development',
|
||||
name: mode,
|
||||
prod: mode === 'production',
|
||||
},
|
||||
packageInfo: {
|
||||
branch: 'some-branch',
|
||||
buildNum: 1,
|
||||
buildSha: 'some-sha-256',
|
||||
version: 'some-version',
|
||||
...packageInfo,
|
||||
configs: options.configs || [],
|
||||
cliArgs: {
|
||||
dev: true,
|
||||
...(options.cliArgs || {}),
|
||||
},
|
||||
isDevClusterMaster:
|
||||
options.isDevClusterMaster !== undefined ? options.isDevClusterMaster : false,
|
||||
};
|
||||
}
|
||||
|
|
149
src/core/server/config/__tests__/__snapshots__/env.test.ts.snap
Normal file
149
src/core/server/config/__tests__/__snapshots__/env.test.ts.snap
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`correctly creates default environment in dev mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": true,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/test/cwd/config/kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": true,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": true,
|
||||
"name": "development",
|
||||
"prod": false,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "some-branch",
|
||||
"buildNum": 9007199254740991,
|
||||
"buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"version": "some-version",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates default environment in prod distributable mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 100,
|
||||
"buildSha": "feature-v1-build-sha",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates default environment in prod non-distributable mode.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/test/cwd/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/test/cwd/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/test/cwd/core_plugins",
|
||||
"homeDir": "/test/cwd",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/test/cwd/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 9007199254740991,
|
||||
"buildSha": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/test/cwd/ui",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`correctly creates environment with constructor.: env properties 1`] = `
|
||||
Env {
|
||||
"binDir": "/some/home/dir/bin",
|
||||
"cliArgs": Object {
|
||||
"dev": false,
|
||||
"someArg": 1,
|
||||
"someOtherArg": "2",
|
||||
},
|
||||
"configDir": "/some/home/dir/config",
|
||||
"configs": Array [
|
||||
"/some/other/path/some-kibana.yml",
|
||||
],
|
||||
"corePluginsDir": "/some/home/dir/core_plugins",
|
||||
"homeDir": "/some/home/dir",
|
||||
"isDevClusterMaster": false,
|
||||
"legacy": EventEmitter {
|
||||
"_events": Object {},
|
||||
"_eventsCount": 0,
|
||||
"_maxListeners": undefined,
|
||||
"domain": null,
|
||||
},
|
||||
"logDir": "/some/home/dir/log",
|
||||
"mode": Object {
|
||||
"dev": false,
|
||||
"name": "production",
|
||||
"prod": true,
|
||||
},
|
||||
"packageInfo": Object {
|
||||
"branch": "feature-v1",
|
||||
"buildNum": 100,
|
||||
"buildSha": "feature-v1-build-sha",
|
||||
"version": "v1",
|
||||
},
|
||||
"staticFilesDir": "/some/home/dir/ui",
|
||||
}
|
||||
`;
|
|
@ -18,8 +18,13 @@
|
|||
*/
|
||||
|
||||
/* tslint:disable max-classes-per-file */
|
||||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../../utils/package_json', () => ({ pkg: mockPackage }));
|
||||
|
||||
import { schema, Type, TypeOf } from '../schema';
|
||||
|
||||
import { ConfigService, ObjectToRawConfigAdapter } from '..';
|
||||
|
@ -161,21 +166,19 @@ test('tracks unhandled paths', async () => {
|
|||
});
|
||||
|
||||
test('correctly passes context', async () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const env = new Env('/kibana', getEnvOptions());
|
||||
|
||||
const config$ = new BehaviorSubject(new ObjectToRawConfigAdapter({ foo: {} }));
|
||||
|
||||
const env = new Env(
|
||||
'/kibana',
|
||||
getEnvOptions({
|
||||
mode: 'development',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const configService = new ConfigService(config$, env, logger);
|
||||
const configs = configService.atPath(
|
||||
'foo',
|
||||
|
|
|
@ -29,76 +29,82 @@ jest.mock('path', () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const mockPackage = new Proxy({ raw: {} as any }, { get: (obj, prop) => obj.raw[prop] });
|
||||
jest.mock('../../../../utils/package_json', () => ({ pkg: mockPackage }));
|
||||
|
||||
import { Env } from '../env';
|
||||
import { getEnvOptions } from './__mocks__/env';
|
||||
|
||||
test('correctly creates default environment with empty options.', () => {
|
||||
const envOptions = getEnvOptions();
|
||||
const defaultEnv = Env.createDefault(envOptions);
|
||||
test('correctly creates default environment in dev mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'some-branch',
|
||||
version: 'some-version',
|
||||
};
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/test/cwd');
|
||||
expect(defaultEnv.configDir).toEqual('/test/cwd/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/test/cwd/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/test/cwd/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/test/cwd/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/test/cwd/ui');
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: true, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/test/cwd/config/kibana.yml'],
|
||||
isDevClusterMaster: true,
|
||||
});
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual('/test/cwd/config/kibana.yml');
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBeUndefined();
|
||||
expect(defaultEnv.getMode()).toEqual(envOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(envOptions.packageInfo);
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
test('correctly creates default environment with options overrides.', () => {
|
||||
const mockEnvOptions = getEnvOptions({
|
||||
config: '/some/other/path/some-kibana.yml',
|
||||
kbnServer: {},
|
||||
mode: 'production',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
test('correctly creates default environment in prod distributable mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
const defaultEnv = Env.createDefault(mockEnvOptions);
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/test/cwd');
|
||||
expect(defaultEnv.configDir).toEqual('/test/cwd/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/test/cwd/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/test/cwd/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/test/cwd/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/test/cwd/ui');
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual(mockEnvOptions.config);
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBe(mockEnvOptions.kbnServer);
|
||||
expect(defaultEnv.getMode()).toEqual(mockEnvOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(mockEnvOptions.packageInfo);
|
||||
test('correctly creates default environment in prod non-distributable mode.', () => {
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: false,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const defaultEnv = Env.createDefault({
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
|
||||
expect(defaultEnv).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
||||
test('correctly creates environment with constructor.', () => {
|
||||
const mockEnvOptions = getEnvOptions({
|
||||
config: '/some/other/path/some-kibana.yml',
|
||||
mode: 'production',
|
||||
packageInfo: {
|
||||
branch: 'feature-v1',
|
||||
buildNum: 100,
|
||||
buildSha: 'feature-v1-build-sha',
|
||||
version: 'v1',
|
||||
mockPackage.raw = {
|
||||
branch: 'feature-v1',
|
||||
version: 'v1',
|
||||
build: {
|
||||
distributable: true,
|
||||
number: 100,
|
||||
sha: 'feature-v1-build-sha',
|
||||
},
|
||||
};
|
||||
|
||||
const env = new Env('/some/home/dir', {
|
||||
cliArgs: { dev: false, someArg: 1, someOtherArg: '2' },
|
||||
configs: ['/some/other/path/some-kibana.yml'],
|
||||
isDevClusterMaster: false,
|
||||
});
|
||||
|
||||
const defaultEnv = new Env('/some/home/dir', mockEnvOptions);
|
||||
|
||||
expect(defaultEnv.homeDir).toEqual('/some/home/dir');
|
||||
expect(defaultEnv.configDir).toEqual('/some/home/dir/config');
|
||||
expect(defaultEnv.corePluginsDir).toEqual('/some/home/dir/core_plugins');
|
||||
expect(defaultEnv.binDir).toEqual('/some/home/dir/bin');
|
||||
expect(defaultEnv.logDir).toEqual('/some/home/dir/log');
|
||||
expect(defaultEnv.staticFilesDir).toEqual('/some/home/dir/ui');
|
||||
|
||||
expect(defaultEnv.getConfigFile()).toEqual(mockEnvOptions.config);
|
||||
expect(defaultEnv.getLegacyKbnServer()).toBeUndefined();
|
||||
expect(defaultEnv.getMode()).toEqual(mockEnvOptions.mode);
|
||||
expect(defaultEnv.getPackageInfo()).toEqual(mockEnvOptions.packageInfo);
|
||||
expect(env).toMatchSnapshot('env properties');
|
||||
});
|
||||
|
|
|
@ -138,13 +138,12 @@ export class ConfigService {
|
|||
);
|
||||
}
|
||||
|
||||
const environmentMode = this.env.getMode();
|
||||
const config = ConfigClass.schema.validate(
|
||||
rawConfig,
|
||||
{
|
||||
dev: environmentMode.dev,
|
||||
prod: environmentMode.prod,
|
||||
...this.env.getPackageInfo(),
|
||||
dev: this.env.mode.dev,
|
||||
prod: this.env.mode.prod,
|
||||
...this.env.packageInfo,
|
||||
},
|
||||
namespace
|
||||
);
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { resolve } from 'path';
|
||||
import process from 'process';
|
||||
|
||||
import { LegacyKbnServer } from '../legacy_compat';
|
||||
import { pkg } from '../../../utils/package_json';
|
||||
|
||||
interface PackageInfo {
|
||||
version: string;
|
||||
|
@ -36,11 +37,9 @@ interface EnvironmentMode {
|
|||
}
|
||||
|
||||
export interface EnvOptions {
|
||||
config?: string;
|
||||
kbnServer?: any;
|
||||
packageInfo: PackageInfo;
|
||||
mode: EnvironmentMode;
|
||||
[key: string]: any;
|
||||
configs: string[];
|
||||
cliArgs: Record<string, any>;
|
||||
isDevClusterMaster: boolean;
|
||||
}
|
||||
|
||||
export class Env {
|
||||
|
@ -57,44 +56,64 @@ export class Env {
|
|||
public readonly logDir: string;
|
||||
public readonly staticFilesDir: string;
|
||||
|
||||
/**
|
||||
* Information about Kibana package (version, build number etc.).
|
||||
*/
|
||||
public readonly packageInfo: Readonly<PackageInfo>;
|
||||
|
||||
/**
|
||||
* Mode Kibana currently run in (development or production).
|
||||
*/
|
||||
public readonly mode: Readonly<EnvironmentMode>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(readonly homeDir: string, private readonly options: EnvOptions) {
|
||||
public readonly legacy: EventEmitter;
|
||||
|
||||
/**
|
||||
* Arguments provided through command line.
|
||||
*/
|
||||
public readonly cliArgs: Readonly<Record<string, any>>;
|
||||
|
||||
/**
|
||||
* Paths to the configuration files.
|
||||
*/
|
||||
public readonly configs: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* Indicates that this Kibana instance is run as development Node Cluster master.
|
||||
*/
|
||||
public readonly isDevClusterMaster: boolean;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(readonly homeDir: string, options: EnvOptions) {
|
||||
this.configDir = resolve(this.homeDir, 'config');
|
||||
this.corePluginsDir = resolve(this.homeDir, 'core_plugins');
|
||||
this.binDir = resolve(this.homeDir, 'bin');
|
||||
this.logDir = resolve(this.homeDir, 'log');
|
||||
this.staticFilesDir = resolve(this.homeDir, 'ui');
|
||||
}
|
||||
|
||||
public getConfigFile() {
|
||||
const defaultConfigFile = this.getDefaultConfigFile();
|
||||
return this.options.config === undefined ? defaultConfigFile : this.options.config;
|
||||
}
|
||||
this.cliArgs = Object.freeze(options.cliArgs);
|
||||
this.configs = Object.freeze(options.configs);
|
||||
this.isDevClusterMaster = options.isDevClusterMaster;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public getLegacyKbnServer(): LegacyKbnServer | undefined {
|
||||
return this.options.kbnServer;
|
||||
}
|
||||
this.mode = Object.freeze<EnvironmentMode>({
|
||||
dev: this.cliArgs.dev,
|
||||
name: this.cliArgs.dev ? 'development' : 'production',
|
||||
prod: !this.cliArgs.dev,
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets information about Kibana package (version, build number etc.).
|
||||
*/
|
||||
public getPackageInfo() {
|
||||
return this.options.packageInfo;
|
||||
}
|
||||
const isKibanaDistributable = pkg.build && pkg.build.distributable === true;
|
||||
this.packageInfo = Object.freeze({
|
||||
branch: pkg.branch,
|
||||
buildNum: isKibanaDistributable ? pkg.build.number : Number.MAX_SAFE_INTEGER,
|
||||
buildSha: isKibanaDistributable ? pkg.build.sha : 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
version: pkg.version,
|
||||
});
|
||||
|
||||
/**
|
||||
* Gets mode Kibana currently run in (development or production).
|
||||
*/
|
||||
public getMode() {
|
||||
return this.options.mode;
|
||||
}
|
||||
|
||||
private getDefaultConfigFile() {
|
||||
return resolve(this.configDir, 'kibana.yml');
|
||||
this.legacy = new EventEmitter();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`broadcasts server and connection options to the legacy "channel" 1`] = `
|
||||
Object {
|
||||
"host": "127.0.0.1",
|
||||
"port": 12345,
|
||||
"routes": Object {
|
||||
"cors": undefined,
|
||||
"payload": Object {
|
||||
"maxBytes": 1024,
|
||||
},
|
||||
"validate": Object {
|
||||
"options": Object {
|
||||
"abortEarly": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
"state": Object {
|
||||
"strictHeader": false,
|
||||
},
|
||||
}
|
||||
`;
|
|
@ -24,7 +24,6 @@ jest.mock('fs', () => ({
|
|||
}));
|
||||
|
||||
import Chance from 'chance';
|
||||
import http from 'http';
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { Env } from '../../config';
|
||||
|
@ -36,6 +35,7 @@ import { Router } from '../router';
|
|||
|
||||
const chance = new Chance();
|
||||
|
||||
let env: Env;
|
||||
let server: HttpServer;
|
||||
let config: HttpConfig;
|
||||
|
||||
|
@ -51,7 +51,8 @@ beforeEach(() => {
|
|||
ssl: {},
|
||||
} as HttpConfig;
|
||||
|
||||
server = new HttpServer(logger.get(), new Env('/kibana', getEnvOptions()));
|
||||
env = new Env('/kibana', getEnvOptions());
|
||||
server = new HttpServer(logger.get(), env);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
@ -563,99 +564,21 @@ describe('with defined `redirectHttpFromPort`', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when run within legacy platform', () => {
|
||||
let newPlatformProxyListenerMock: any;
|
||||
beforeEach(() => {
|
||||
newPlatformProxyListenerMock = {
|
||||
bind: jest.fn(),
|
||||
proxy: jest.fn(),
|
||||
};
|
||||
test('broadcasts server and connection options to the legacy "channel"', async () => {
|
||||
const onConnectionListener = jest.fn();
|
||||
env.legacy.on('connection', onConnectionListener);
|
||||
|
||||
const kbnServerMock = {
|
||||
newPlatformProxyListener: newPlatformProxyListenerMock,
|
||||
};
|
||||
expect(onConnectionListener).not.toHaveBeenCalled();
|
||||
|
||||
server = new HttpServer(
|
||||
logger.get(),
|
||||
new Env('/kibana', getEnvOptions({ kbnServer: kbnServerMock }))
|
||||
);
|
||||
|
||||
const router = new Router('/new');
|
||||
router.get({ path: '/', validate: false }, async (req, res) => {
|
||||
return res.ok({ key: 'new-platform' });
|
||||
});
|
||||
|
||||
server.registerRouter(router);
|
||||
|
||||
newPlatformProxyListenerMock.proxy.mockImplementation(
|
||||
(req: http.IncomingMessage, res: http.ServerResponse) => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ key: `legacy-platform:${req.url}` }));
|
||||
}
|
||||
);
|
||||
await server.start({
|
||||
...config,
|
||||
port: 12345,
|
||||
});
|
||||
|
||||
test('binds proxy listener to server.', async () => {
|
||||
expect(newPlatformProxyListenerMock.bind).not.toHaveBeenCalled();
|
||||
expect(onConnectionListener).toHaveBeenCalledTimes(1);
|
||||
|
||||
await server.start(config);
|
||||
|
||||
expect(newPlatformProxyListenerMock.bind).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.bind).toHaveBeenCalledWith(
|
||||
expect.any((http as any).Server)
|
||||
);
|
||||
expect(newPlatformProxyListenerMock.bind.mock.calls[0][0]).toBe(getServerListener(server));
|
||||
});
|
||||
|
||||
test('forwards request to legacy platform if new one cannot handle it', async () => {
|
||||
await server.start(config);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/legacy')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'legacy-platform:/legacy' });
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledWith(
|
||||
expect.any((http as any).IncomingMessage),
|
||||
expect.any((http as any).ServerResponse)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('forwards request to legacy platform and rewrites base path if needed', async () => {
|
||||
await server.start({
|
||||
...config,
|
||||
basePath: '/bar',
|
||||
rewriteBasePath: true,
|
||||
});
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/legacy')
|
||||
.expect(404);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/bar/legacy')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'legacy-platform:/legacy' });
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledTimes(1);
|
||||
expect(newPlatformProxyListenerMock.proxy).toHaveBeenCalledWith(
|
||||
expect.any((http as any).IncomingMessage),
|
||||
expect.any((http as any).ServerResponse)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('do not forward request to legacy platform if new one can handle it', async () => {
|
||||
await server.start(config);
|
||||
|
||||
await supertest(getServerListener(server))
|
||||
.get('/new/')
|
||||
.expect(200)
|
||||
.then(res => {
|
||||
expect(res.body).toEqual({ key: 'new-platform' });
|
||||
expect(newPlatformProxyListenerMock.proxy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
const [[{ options, server: rawServer }]] = onConnectionListener.mock.calls;
|
||||
expect(rawServer).toBeDefined();
|
||||
expect(rawServer).toBe((server as any).server);
|
||||
expect(options).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -45,7 +45,10 @@ export class HttpServer {
|
|||
}
|
||||
|
||||
public async start(config: HttpConfig) {
|
||||
this.server = createServer(getServerOptions(config));
|
||||
this.log.debug('starting http server');
|
||||
|
||||
const serverOptions = getServerOptions(config);
|
||||
this.server = createServer(serverOptions);
|
||||
|
||||
this.setupBasePathRewrite(this.server, config);
|
||||
|
||||
|
@ -59,32 +62,13 @@ export class HttpServer {
|
|||
}
|
||||
}
|
||||
|
||||
const legacyKbnServer = this.env.getLegacyKbnServer();
|
||||
if (legacyKbnServer !== undefined) {
|
||||
legacyKbnServer.newPlatformProxyListener.bind(this.server.listener);
|
||||
|
||||
// We register Kibana proxy middleware right before we start server to allow
|
||||
// all new platform plugins register their routes, so that `legacyKbnServer`
|
||||
// handles only requests that aren't handled by the new platform.
|
||||
this.server.route({
|
||||
handler: ({ raw: { req, res } }, responseToolkit) => {
|
||||
legacyKbnServer.newPlatformProxyListener.proxy(req, res);
|
||||
return responseToolkit.abandon;
|
||||
},
|
||||
method: '*',
|
||||
options: {
|
||||
payload: {
|
||||
output: 'stream',
|
||||
parse: false,
|
||||
timeout: false,
|
||||
// Having such a large value here will allow legacy routes to override
|
||||
// maximum allowed payload size set in the core http server if needed.
|
||||
maxBytes: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
},
|
||||
path: '/{p*}',
|
||||
});
|
||||
}
|
||||
// Notify legacy compatibility layer about HTTP(S) connection providing server
|
||||
// instance with connection options so that we can properly bridge core and
|
||||
// the "legacy" Kibana internally.
|
||||
this.env.legacy.emit('connection', {
|
||||
options: serverOptions,
|
||||
server: this.server,
|
||||
});
|
||||
|
||||
await this.server.start();
|
||||
|
||||
|
@ -96,12 +80,13 @@ export class HttpServer {
|
|||
}
|
||||
|
||||
public async stop() {
|
||||
this.log.info('stopping http server');
|
||||
|
||||
if (this.server !== undefined) {
|
||||
await this.server.stop();
|
||||
this.server = undefined;
|
||||
if (this.server === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.log.debug('stopping http server');
|
||||
await this.server.stop();
|
||||
this.server = undefined;
|
||||
}
|
||||
|
||||
private setupBasePathRewrite(server: Server, config: HttpConfig) {
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`correctly unbinds from the previous server. 1`] = `"Unhandled \\"error\\" event. (Error: Some error)"`;
|
||||
exports[`correctly binds to the server.: proxy route options 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"handler": [Function],
|
||||
"method": "*",
|
||||
"options": Object {
|
||||
"payload": Object {
|
||||
"maxBytes": 9007199254740991,
|
||||
"output": "stream",
|
||||
"parse": false,
|
||||
"timeout": false,
|
||||
},
|
||||
},
|
||||
"path": "/{p*}",
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { LegacyKbnServer } from '..';
|
||||
|
||||
test('correctly returns `newPlatformProxyListener`.', () => {
|
||||
const rawKbnServer = {
|
||||
newPlatform: {
|
||||
proxyListener: {},
|
||||
},
|
||||
};
|
||||
|
||||
const legacyKbnServer = new LegacyKbnServer(rawKbnServer);
|
||||
expect(legacyKbnServer.newPlatformProxyListener).toBe(rawKbnServer.newPlatform.proxyListener);
|
||||
});
|
|
@ -17,131 +17,74 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
|
||||
class MockNetServer extends EventEmitter {
|
||||
public address() {
|
||||
return { port: 1234, family: 'test-family', address: 'test-address' };
|
||||
}
|
||||
|
||||
public getConnections(callback: (error: Error | null, count: number) => void) {
|
||||
callback(null, 100500);
|
||||
}
|
||||
}
|
||||
|
||||
function mockNetServer() {
|
||||
return new MockNetServer();
|
||||
}
|
||||
|
||||
jest.mock('net', () => ({
|
||||
createServer: jest.fn(() => mockNetServer()),
|
||||
}));
|
||||
|
||||
import { createServer } from 'net';
|
||||
import { Server as HapiServer } from 'hapi-latest';
|
||||
import { Server } from 'net';
|
||||
import { LegacyPlatformProxifier } from '..';
|
||||
import { Env } from '../../config';
|
||||
import { getEnvOptions } from '../../config/__tests__/__mocks__/env';
|
||||
import { logger } from '../../logging/__mocks__';
|
||||
|
||||
let server: jest.Mocked<Server>;
|
||||
let mockHapiServer: jest.Mocked<HapiServer>;
|
||||
let root: any;
|
||||
let proxifier: LegacyPlatformProxifier;
|
||||
beforeEach(() => {
|
||||
server = {
|
||||
addListener: jest.fn(),
|
||||
address: jest
|
||||
.fn()
|
||||
.mockReturnValue({ port: 1234, family: 'test-family', address: 'test-address' }),
|
||||
getConnections: jest.fn(),
|
||||
} as any;
|
||||
|
||||
mockHapiServer = { listener: server, route: jest.fn() } as any;
|
||||
|
||||
root = {
|
||||
logger: {
|
||||
get: jest.fn(() => ({
|
||||
debug: jest.fn(),
|
||||
info: jest.fn(),
|
||||
})),
|
||||
},
|
||||
logger,
|
||||
shutdown: jest.fn(),
|
||||
start: jest.fn(),
|
||||
} as any;
|
||||
|
||||
proxifier = new LegacyPlatformProxifier(root);
|
||||
const env = new Env('/kibana', getEnvOptions());
|
||||
proxifier = new LegacyPlatformProxifier(root, env);
|
||||
env.legacy.emit('connection', {
|
||||
server: mockHapiServer,
|
||||
options: { someOption: 'foo', someAnotherOption: 'bar' },
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly binds to the server.', () => {
|
||||
const server = createServer();
|
||||
jest.spyOn(server, 'addListener');
|
||||
proxifier.bind(server);
|
||||
|
||||
expect(server.addListener).toHaveBeenCalledTimes(4);
|
||||
for (const eventName of ['listening', 'error', 'clientError', 'connection']) {
|
||||
expect(mockHapiServer.route.mock.calls).toMatchSnapshot('proxy route options');
|
||||
expect(server.addListener).toHaveBeenCalledTimes(6);
|
||||
for (const eventName of ['clientError', 'close', 'connection', 'error', 'listening', 'upgrade']) {
|
||||
expect(server.addListener).toHaveBeenCalledWith(eventName, expect.any(Function));
|
||||
}
|
||||
});
|
||||
|
||||
test('correctly binds to the server and redirects its events.', () => {
|
||||
const server = createServer();
|
||||
proxifier.bind(server);
|
||||
test('correctly redirects server events.', () => {
|
||||
for (const eventName of ['clientError', 'close', 'connection', 'error', 'listening', 'upgrade']) {
|
||||
expect(server.addListener).toHaveBeenCalledWith(eventName, expect.any(Function));
|
||||
|
||||
const eventsAndListeners = new Map(
|
||||
['listening', 'error', 'clientError', 'connection'].map(eventName => {
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
return [eventName, listener] as [string, () => void];
|
||||
})
|
||||
);
|
||||
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
// Emit several events, to make sure that server is not being listened with `once`.
|
||||
server.emit(eventName, 1, 2, 3, 4);
|
||||
server.emit(eventName, 5, 6, 7, 8);
|
||||
const [, serverListener] = server.addListener.mock.calls.find(
|
||||
([serverEventName]) => serverEventName === eventName
|
||||
)!;
|
||||
|
||||
serverListener(1, 2, 3, 4);
|
||||
serverListener(5, 6, 7, 8);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(2);
|
||||
expect(listener).toHaveBeenCalledWith(1, 2, 3, 4);
|
||||
expect(listener).toHaveBeenCalledWith(5, 6, 7, 8);
|
||||
}
|
||||
});
|
||||
|
||||
test('correctly unbinds from the previous server.', () => {
|
||||
const previousServer = createServer();
|
||||
proxifier.bind(previousServer);
|
||||
|
||||
const currentServer = createServer();
|
||||
proxifier.bind(currentServer);
|
||||
|
||||
const eventsAndListeners = new Map(
|
||||
['listening', 'error', 'clientError', 'connection'].map(eventName => {
|
||||
const listener = jest.fn();
|
||||
proxifier.addListener(eventName, listener);
|
||||
|
||||
return [eventName, listener] as [string, () => void];
|
||||
})
|
||||
);
|
||||
|
||||
// Any events from the previous server should not be forwarded.
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
// `error` event is a special case in node, if `error` is emitted, but
|
||||
// there is no listener for it error will be thrown.
|
||||
if (eventName === 'error') {
|
||||
expect(() =>
|
||||
previousServer.emit(eventName, new Error('Some error'))
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
} else {
|
||||
previousServer.emit(eventName, 1, 2, 3, 4);
|
||||
}
|
||||
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
// Only events from the last server should be forwarded.
|
||||
for (const [eventName, listener] of eventsAndListeners) {
|
||||
expect(listener).not.toHaveBeenCalled();
|
||||
|
||||
currentServer.emit(eventName, 1, 2, 3, 4);
|
||||
|
||||
expect(listener).toHaveBeenCalledTimes(1);
|
||||
expect(listener).toHaveBeenCalledWith(1, 2, 3, 4);
|
||||
proxifier.removeListener(eventName, listener);
|
||||
}
|
||||
});
|
||||
|
||||
test('returns `address` from the underlying server.', () => {
|
||||
expect(proxifier.address()).toBeUndefined();
|
||||
|
||||
proxifier.bind(createServer());
|
||||
|
||||
expect(proxifier.address()).toEqual({
|
||||
address: 'test-address',
|
||||
family: 'test-family',
|
||||
|
@ -168,33 +111,35 @@ test('`close` shuts down the `root`.', async () => {
|
|||
});
|
||||
|
||||
test('returns connection count from the underlying server.', () => {
|
||||
server.getConnections.mockImplementation(callback => callback(null, 0));
|
||||
const onGetConnectionsComplete = jest.fn();
|
||||
|
||||
proxifier.getConnections(onGetConnectionsComplete);
|
||||
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledTimes(1);
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledWith(null, 0);
|
||||
onGetConnectionsComplete.mockReset();
|
||||
|
||||
proxifier.bind(createServer());
|
||||
server.getConnections.mockImplementation(callback => callback(null, 100500));
|
||||
proxifier.getConnections(onGetConnectionsComplete);
|
||||
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledTimes(1);
|
||||
expect(onGetConnectionsComplete).toHaveBeenCalledWith(null, 100500);
|
||||
});
|
||||
|
||||
test('correctly proxies request and response objects.', () => {
|
||||
test('proxy route abandons request processing and forwards it to the legacy Kibana', async () => {
|
||||
const mockResponseToolkit = { response: jest.fn(), abandon: Symbol('abandon') };
|
||||
const mockRequest = { raw: { req: { a: 1 }, res: { b: 2 } } };
|
||||
|
||||
const onRequest = jest.fn();
|
||||
proxifier.addListener('request', onRequest);
|
||||
|
||||
const request = {} as IncomingMessage;
|
||||
const response = {} as ServerResponse;
|
||||
proxifier.proxy(request, response);
|
||||
const [[{ handler }]] = mockHapiServer.route.mock.calls;
|
||||
const response = await handler(mockRequest, mockResponseToolkit);
|
||||
|
||||
expect(response).toBe(mockResponseToolkit.abandon);
|
||||
expect(mockResponseToolkit.response).not.toHaveBeenCalled();
|
||||
|
||||
// Make sure request hasn't been passed to the legacy platform.
|
||||
expect(onRequest).toHaveBeenCalledTimes(1);
|
||||
expect(onRequest).toHaveBeenCalledWith(request, response);
|
||||
|
||||
// Check that exactly same objects were passed as event arguments.
|
||||
expect(onRequest.mock.calls[0][0]).toBe(request);
|
||||
expect(onRequest.mock.calls[0][1]).toBe(response);
|
||||
expect(onRequest).toHaveBeenCalledWith(mockRequest.raw.req, mockRequest.raw.res);
|
||||
});
|
||||
|
|
|
@ -19,24 +19,18 @@
|
|||
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
/** @internal */
|
||||
export { LegacyPlatformProxifier } from './legacy_platform_proxifier';
|
||||
/** @internal */
|
||||
export { LegacyConfigToRawConfigAdapter, LegacyConfig } from './legacy_platform_config';
|
||||
/** @internal */
|
||||
export { LegacyKbnServer } from './legacy_kbn_server';
|
||||
|
||||
import {
|
||||
LegacyConfig,
|
||||
LegacyConfigToRawConfigAdapter,
|
||||
LegacyKbnServer,
|
||||
LegacyPlatformProxifier,
|
||||
} from '.';
|
||||
import { LegacyConfig, LegacyConfigToRawConfigAdapter, LegacyPlatformProxifier } from '.';
|
||||
import { Env } from '../config';
|
||||
import { Root } from '../root';
|
||||
import { BasePathProxyRoot } from '../root/base_path_proxy_root';
|
||||
|
||||
function initEnvironment(rawKbnServer: any) {
|
||||
function initEnvironment(rawKbnServer: any, isDevClusterMaster = false) {
|
||||
const config: LegacyConfig = rawKbnServer.config;
|
||||
|
||||
const legacyConfig$ = new BehaviorSubject(config);
|
||||
|
@ -45,12 +39,12 @@ function initEnvironment(rawKbnServer: any) {
|
|||
);
|
||||
|
||||
const env = Env.createDefault({
|
||||
kbnServer: new LegacyKbnServer(rawKbnServer),
|
||||
// The defaults for the following parameters are retrieved by the legacy
|
||||
// platform from the command line or from `package.json` and stored in the
|
||||
// config, so we can borrow these parameters and avoid double parsing.
|
||||
mode: config.get('env'),
|
||||
packageInfo: config.get('pkg'),
|
||||
// The core doesn't work with configs yet, everything is provided by the
|
||||
// "legacy" Kibana, so we can have empty array here.
|
||||
configs: [],
|
||||
// `dev` is the only CLI argument we currently use.
|
||||
cliArgs: { dev: config.get('env.dev') },
|
||||
isDevClusterMaster,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -71,12 +65,12 @@ export const injectIntoKbnServer = (rawKbnServer: any) => {
|
|||
|
||||
rawKbnServer.newPlatform = {
|
||||
// Custom HTTP Listener that will be used within legacy platform by HapiJS server.
|
||||
proxyListener: new LegacyPlatformProxifier(new Root(config$, env)),
|
||||
proxyListener: new LegacyPlatformProxifier(new Root(config$, env), env),
|
||||
updateConfig,
|
||||
};
|
||||
};
|
||||
|
||||
export const createBasePathProxy = (rawKbnServer: any) => {
|
||||
const { env, config$ } = initEnvironment(rawKbnServer);
|
||||
const { env, config$ } = initEnvironment(rawKbnServer, true /*isDevClusterMaster*/);
|
||||
return new BasePathProxyRoot(config$, env);
|
||||
};
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a wrapper around legacy `kbnServer` instance that exposes only
|
||||
* a subset of `kbnServer` APIs used by the new platform.
|
||||
* @internal
|
||||
*/
|
||||
export class LegacyKbnServer {
|
||||
constructor(private readonly rawKbnServer: any) {}
|
||||
|
||||
/**
|
||||
* Custom HTTP Listener used by HapiJS server in the legacy platform.
|
||||
*/
|
||||
get newPlatformProxyListener() {
|
||||
return this.rawKbnServer.newPlatform.proxyListener;
|
||||
}
|
||||
}
|
|
@ -18,16 +18,29 @@
|
|||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { Server } from 'net';
|
||||
|
||||
import { Server as HapiServer, ServerOptions as HapiServerOptions } from 'hapi-latest';
|
||||
import { Env } from '../config';
|
||||
import { Logger } from '../logging';
|
||||
import { Root } from '../root';
|
||||
|
||||
interface ConnectionInfo {
|
||||
server: HapiServer;
|
||||
options: HapiServerOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of the server events to be forwarded to the legacy platform.
|
||||
*/
|
||||
const ServerEventsToForward = ['listening', 'error', 'clientError', 'connection'];
|
||||
const ServerEventsToForward = [
|
||||
'clientError',
|
||||
'close',
|
||||
'connection',
|
||||
'error',
|
||||
'listening',
|
||||
'upgrade',
|
||||
];
|
||||
|
||||
/**
|
||||
* Represents "proxy" between legacy and current platform.
|
||||
|
@ -38,7 +51,7 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
private readonly log: Logger;
|
||||
private server?: Server;
|
||||
|
||||
constructor(private readonly root: Root) {
|
||||
constructor(private readonly root: Root, private readonly env: Env) {
|
||||
super();
|
||||
|
||||
this.log = root.logger.get('legacy-platform-proxifier');
|
||||
|
@ -56,6 +69,14 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
] as [string, (...args: any[]) => void];
|
||||
})
|
||||
);
|
||||
|
||||
// Once core HTTP service is ready it broadcasts the internal server it relies on
|
||||
// and server options that were used to create that server so that we can properly
|
||||
// bridge with the "legacy" Kibana. If server isn't run (e.g. if process is managed
|
||||
// by ClusterManager or optimizer) then this event will never fire.
|
||||
this.env.legacy.once('connection', (connectionInfo: ConnectionInfo) =>
|
||||
this.onConnection(connectionInfo)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,31 +137,36 @@ export class LegacyPlatformProxifier extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds Http/Https server to the LegacyPlatformProxifier.
|
||||
* @param server Server to bind to.
|
||||
*/
|
||||
public bind(server: Server) {
|
||||
const oldServer = this.server;
|
||||
this.server = server;
|
||||
private onConnection({ server }: ConnectionInfo) {
|
||||
this.server = server.listener;
|
||||
|
||||
for (const [eventName, eventHandler] of this.eventHandlers) {
|
||||
if (oldServer !== undefined) {
|
||||
oldServer.removeListener(eventName, eventHandler);
|
||||
}
|
||||
|
||||
this.server.addListener(eventName, eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards request and response objects to the legacy platform.
|
||||
* This method is used whenever new platform doesn't know how to handle the request.
|
||||
* @param request Native Node request object instance.
|
||||
* @param response Native Node response object instance.
|
||||
*/
|
||||
public proxy(request: IncomingMessage, response: ServerResponse) {
|
||||
this.log.debug(`Request will be handled by proxy ${request.method}:${request.url}.`);
|
||||
this.emit('request', request, response);
|
||||
// We register Kibana proxy middleware right before we start server to allow
|
||||
// all new platform plugins register their routes, so that `legacyProxy`
|
||||
// handles only requests that aren't handled by the new platform.
|
||||
server.route({
|
||||
path: '/{p*}',
|
||||
method: '*',
|
||||
options: {
|
||||
payload: {
|
||||
output: 'stream',
|
||||
parse: false,
|
||||
timeout: false,
|
||||
// Having such a large value here will allow legacy routes to override
|
||||
// maximum allowed payload size set in the core http server if needed.
|
||||
maxBytes: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
},
|
||||
handler: async ({ raw: { req, res } }, responseToolkit) => {
|
||||
this.log.trace(`Request will be handled by proxy ${req.method}:${req.url}.`);
|
||||
// Forward request and response objects to the legacy platform. This method
|
||||
// is used whenever new platform doesn't know how to handle the request.
|
||||
this.emit('request', req, res);
|
||||
return responseToolkit.abandon;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
import chrome from 'ui/chrome';
|
||||
import { notify } from 'ui/notify';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
||||
const baseUrl = chrome.addBasePath('/api/kibana/home/tutorials');
|
||||
const headers = new Headers();
|
||||
|
@ -44,7 +44,10 @@ async function loadTutorials() {
|
|||
tutorials = await response.json();
|
||||
tutorialsLoaded = true;
|
||||
} catch(err) {
|
||||
notify.error(`Unable to load tutorials, ${err}`);
|
||||
toastNotifications.addDanger({
|
||||
title: 'Unable to load tutorials',
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import './index_header';
|
|||
import './create_edit_field';
|
||||
import { KbnUrlProvider } from 'ui/url';
|
||||
import { IndicesEditSectionsProvider } from './edit_sections';
|
||||
import { fatalError } from 'ui/notify';
|
||||
import { fatalError, toastNotifications } from 'ui/notify';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { uiModules } from 'ui/modules';
|
||||
import template from './edit_index_pattern.html';
|
||||
|
@ -181,8 +181,7 @@ uiRoutes
|
|||
|
||||
uiModules.get('apps/management')
|
||||
.controller('managementIndicesEdit', function (
|
||||
$scope, $location, $route, config, indexPatterns, Notifier, Private, AppState, docTitle, confirmModal) {
|
||||
const notify = new Notifier();
|
||||
$scope, $location, $route, config, indexPatterns, Private, AppState, docTitle, confirmModal) {
|
||||
const $state = $scope.state = new AppState();
|
||||
const { fieldWildcardMatcher } = Private(FieldWildcardProvider);
|
||||
|
||||
|
@ -292,7 +291,7 @@ uiModules.get('apps/management')
|
|||
const errorMessage = i18n.translate('kbn.management.editIndexPattern.notDateErrorMessage', {
|
||||
defaultMessage: 'That field is a {fieldType} not a date.', values: { fieldType: field.type }
|
||||
});
|
||||
notify.error(errorMessage);
|
||||
toastNotifications.addDanger(errorMessage);
|
||||
return;
|
||||
}
|
||||
$scope.indexPattern.timeFieldName = field.name;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function cephMetricsSpecProvider() {
|
|||
const moduleName = 'ceph';
|
||||
return {
|
||||
id: 'cephMetrics',
|
||||
name: 'Ceph metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.cephMetrics.nameTitle', {
|
||||
defaultMessage: 'Ceph metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from the Ceph server.',
|
||||
longDescription: 'The `ceph` Metricbeat module fetches internal metrics from Ceph.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-ceph.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.cephMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from the Ceph server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.cephMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `ceph` Metricbeat module fetches internal metrics from Ceph. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-ceph.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.cephMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function couchbaseMetricsSpecProvider() {
|
|||
const moduleName = 'couchbase';
|
||||
return {
|
||||
id: 'couchbaseMetrics',
|
||||
name: 'Couchbase metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.couchbaseMetrics.nameTitle', {
|
||||
defaultMessage: 'Couchbase metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from Couchbase.',
|
||||
longDescription: 'The `couchbase` Metricbeat module fetches internal metrics from Couchbase.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-couchbase.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.couchbaseMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from Couchbase.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.couchbaseMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `couchbase` Metricbeat module fetches internal metrics from Couchbase. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-couchbase.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.couchbaseMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,17 +25,29 @@ export function dockerMetricsSpecProvider() {
|
|||
const moduleName = 'docker';
|
||||
return {
|
||||
id: 'dockerMetrics',
|
||||
name: 'Docker metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.dockerMetrics.nameTitle', {
|
||||
defaultMessage: 'Docker metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch metrics about your Docker containers.',
|
||||
longDescription: 'The `docker` Metricbeat module fetches metrics from the Docker server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-docker.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.dockerMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch metrics about your Docker containers.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.dockerMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `docker` Metricbeat module fetches metrics from the Docker server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-docker.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoDocker',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'AV4REOpp5NkDleZmzKkE',
|
||||
linkLabel: 'Docker metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.dockerMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'Docker metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function dropwizardMetricsSpecProvider() {
|
|||
const moduleName = 'dropwizard';
|
||||
return {
|
||||
id: 'dropwizardMetrics',
|
||||
name: 'Dropwizard metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.dropwizardMetrics.nameTitle', {
|
||||
defaultMessage: 'Dropwizard metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from Dropwizard Java application.',
|
||||
longDescription: 'The `dropwizard` Metricbeat module fetches internal metrics from Dropwizard Java Application.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-dropwizard.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.dropwizardMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from Dropwizard Java application.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.dropwizardMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `dropwizard` Metricbeat module fetches internal metrics from Dropwizard Java Application. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-dropwizard.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.dropwizardMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/filebeat_instructions';
|
||||
|
||||
|
@ -27,16 +28,28 @@ export function elasticsearchLogsSpecProvider() {
|
|||
const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'];
|
||||
return {
|
||||
id: 'elasticsearchLogs',
|
||||
name: 'Elasticsearch logs',
|
||||
name: i18n.translate('kbn.server.tutorials.elasticsearchLogs.nameTitle', {
|
||||
defaultMessage: 'Elasticsearch logs',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.LOGGING,
|
||||
isBeta: true,
|
||||
shortDescription: 'Collect and parse logs created by Elasticsearch.',
|
||||
longDescription: 'The `elasticsearch` Filebeat module parses logs created by Elasticsearch.' +
|
||||
' [Learn more]({config.docs.beats.filebeat}/filebeat-module-elasticsearch.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.elasticsearchLogs.shortDescription', {
|
||||
defaultMessage: 'Collect and parse logs created by Elasticsearch.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.elasticsearchLogs.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `elasticsearch` Filebeat module parses logs created by Elasticsearch. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-elasticsearch.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoElasticsearch',
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.elasticsearchLogs.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,17 +25,29 @@ export function mongodbMetricsSpecProvider() {
|
|||
const moduleName = 'mongodb';
|
||||
return {
|
||||
id: 'mongodbMetrics',
|
||||
name: 'MongoDB metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.mongodbMetrics.nameTitle', {
|
||||
defaultMessage: 'MongoDB metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from MongoDB.',
|
||||
longDescription: 'The `mongodb` Metricbeat module fetches internal metrics from the MongoDB server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-mongodb.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.mongodbMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from MongoDB.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.mongodbMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `mongodb` Metricbeat module fetches internal metrics from the MongoDB server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mongodb.html',
|
||||
},
|
||||
}),
|
||||
//euiIconType: 'logoMongoDB',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'Metricbeat-MongoDB',
|
||||
linkLabel: 'MongoDB metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.mongodbMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'MongoDB metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function muninMetricsSpecProvider() {
|
|||
const moduleName = 'munin';
|
||||
return {
|
||||
id: 'muninMetrics',
|
||||
name: 'Munin metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.muninMetrics.nameTitle', {
|
||||
defaultMessage: 'Munin metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from the Munin server.',
|
||||
longDescription: 'The `munin` Metricbeat module fetches internal metrics from Munin.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-munin.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.muninMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from the Munin server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.muninMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `munin` Metricbeat module fetches internal metrics from Munin. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-munin.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.muninMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/filebeat_instructions';
|
||||
|
||||
|
@ -27,17 +28,29 @@ export function mysqlLogsSpecProvider() {
|
|||
const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'];
|
||||
return {
|
||||
id: 'mysqlLogs',
|
||||
name: 'MySQL logs',
|
||||
name: i18n.translate('kbn.server.tutorials.mysqlLogs.nameTitle', {
|
||||
defaultMessage: 'MySQL logs',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.LOGGING,
|
||||
shortDescription: 'Collect and parse error and slow logs created by MySQL.',
|
||||
longDescription: 'The `mysql` Filebeat module parses error and slow logs created by MySQL.' +
|
||||
' [Learn more]({config.docs.beats.filebeat}/filebeat-module-mysql.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.mysqlLogs.shortDescription', {
|
||||
defaultMessage: 'Collect and parse error and slow logs created by MySQL.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.mysqlLogs.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `mysql` Filebeat module parses error and slow logs created by MySQL. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-mysql.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoMySQL',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'Filebeat-MySQL-Dashboard',
|
||||
linkLabel: 'MySQL logs dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.mysqlLogs.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'MySQL logs dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,17 +25,29 @@ export function mysqlMetricsSpecProvider() {
|
|||
const moduleName = 'mysql';
|
||||
return {
|
||||
id: 'mysqlMetrics',
|
||||
name: 'MySQL metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.mysqlMetrics.nameTitle', {
|
||||
defaultMessage: 'MySQL metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from MySQL.',
|
||||
longDescription: 'The `mysql` Metricbeat module fetches internal metrics from the MySQL server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-mysql.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.mysqlMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from MySQL.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.mysqlMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `mysql` Metricbeat module fetches internal metrics from the MySQL server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mysql.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoMySQL',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: '66881e90-0006-11e7-bf7f-c9acc3d3e306',
|
||||
linkLabel: 'MySQL metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.mysqlMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'MySQL metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/filebeat_instructions';
|
||||
|
||||
|
@ -27,17 +28,29 @@ export function nginxLogsSpecProvider() {
|
|||
const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'];
|
||||
return {
|
||||
id: 'nginxLogs',
|
||||
name: 'Nginx logs',
|
||||
name: i18n.translate('kbn.server.tutorials.nginxLogs.nameTitle', {
|
||||
defaultMessage: 'Nginx logs',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.LOGGING,
|
||||
shortDescription: 'Collect and parse access and error logs created by the Nginx HTTP server.',
|
||||
longDescription: 'The `nginx` Filebeat module parses access and error logs created by the Nginx HTTP server.' +
|
||||
' [Learn more]({config.docs.beats.filebeat}/filebeat-module-nginx.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.nginxLogs.shortDescription', {
|
||||
defaultMessage: 'Collect and parse access and error logs created by the Nginx HTTP server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.nginxLogs.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `nginx` Filebeat module parses access and error logs created by the Nginx HTTP server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-nginx.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoNginx',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: '55a9e6e0-a29e-11e7-928f-5dbe6f6f5519',
|
||||
linkLabel: 'Nginx logs dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.nginxLogs.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'Nginx logs dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,20 +25,33 @@ export function nginxMetricsSpecProvider() {
|
|||
const moduleName = 'nginx';
|
||||
return {
|
||||
id: 'nginxMetrics',
|
||||
name: 'Nginx metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.nginxMetrics.nameTitle', {
|
||||
defaultMessage: 'Nginx metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from the Nginx HTTP server.',
|
||||
longDescription: 'The `nginx` Metricbeat module fetches internal metrics from the Nginx HTTP server.' +
|
||||
' The module scrapes the server status data from the web page generated by the' +
|
||||
' [ngx_http_stub_status_module](http://nginx.org/en/docs/http/ngx_http_stub_status_module.html),' +
|
||||
' which must be enabled in your Nginx installation.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-nginx.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.nginxMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from the Nginx HTTP server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.nginxMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `nginx` Metricbeat module fetches internal metrics from the Nginx HTTP server. \
|
||||
The module scrapes the server status data from the web page generated by the \
|
||||
{statusModuleLink}, \
|
||||
which must be enabled in your Nginx installation. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
statusModuleLink: '[ngx_http_stub_status_module](http://nginx.org/en/docs/http/ngx_http_stub_status_module.html)',
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-nginx.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoNginx',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: '023d2930-f1a5-11e7-a9ef-93c69af7b129',
|
||||
linkLabel: 'Nginx metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.nginxMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'Nginx metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,17 +25,29 @@ export function redisMetricsSpecProvider() {
|
|||
const moduleName = 'redis';
|
||||
return {
|
||||
id: 'redisMetrics',
|
||||
name: 'Redis metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.redisMetrics.nameTitle', {
|
||||
defaultMessage: 'Redis metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from Redis.',
|
||||
longDescription: 'The `redis` Metricbeat module fetches internal metrics from the Redis server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-redis.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.redisMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from Redis.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.redisMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `redis` Metricbeat module fetches internal metrics from the Redis server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-redis.html',
|
||||
},
|
||||
}),
|
||||
euiIconType: 'logoRedis',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'AV4YjZ5pux-M-tCAunxK',
|
||||
linkLabel: 'Redis metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.redisMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'Redis metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/filebeat_instructions';
|
||||
|
||||
|
@ -27,17 +28,29 @@ export function systemLogsSpecProvider() {
|
|||
const platforms = ['OSX', 'DEB', 'RPM'];
|
||||
return {
|
||||
id: 'systemLogs',
|
||||
name: 'System logs',
|
||||
name: i18n.translate('kbn.server.tutorials.systemLogs.nameTitle', {
|
||||
defaultMessage: 'System logs',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.LOGGING,
|
||||
shortDescription: 'Collect and parse logs written by the local Syslog server.',
|
||||
longDescription: 'The `system` Filebeat module collects and parses logs created by the system logging service of common ' +
|
||||
' Unix/Linux based distributions. This module is not available on Windows.' +
|
||||
' [Learn more]({config.docs.beats.filebeat}/filebeat-module-system.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.systemLogs.shortDescription', {
|
||||
defaultMessage: 'Collect and parse logs written by the local Syslog server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.systemLogs.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `system` Filebeat module collects and parses logs created by the system logging service of common \
|
||||
Unix/Linux based distributions. This module is not available on Windows. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-system.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'Filebeat-syslog-dashboard',
|
||||
linkLabel: 'System logs dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.systemLogs.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'System logs dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,17 +25,29 @@ export function systemMetricsSpecProvider() {
|
|||
const moduleName = 'system';
|
||||
return {
|
||||
id: 'systemMetrics',
|
||||
name: 'System metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.systemMetrics.nameTitle', {
|
||||
defaultMessage: 'System metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Collect CPU, memory, network, and disk statistics from the host.',
|
||||
longDescription: 'The `system` Metricbeat module collects CPU, memory, network, and disk statistics from the host.' +
|
||||
' It collects system wide statistics and statistics per process and filesystem.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-system.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.systemMetrics.shortDescription', {
|
||||
defaultMessage: 'Collect CPU, memory, network, and disk statistics from the host.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.systemMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `system` Metricbeat module collects CPU, memory, network, and disk statistics from the host. \
|
||||
It collects system wide statistics and statistics per process and filesystem. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-system.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'Metricbeat-system-overview',
|
||||
linkLabel: 'System metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.systemMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'System metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/filebeat_instructions';
|
||||
|
||||
|
@ -27,17 +28,29 @@ export function traefikLogsSpecProvider() {
|
|||
const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'];
|
||||
return {
|
||||
id: 'traefikLogs',
|
||||
name: 'Traefik logs',
|
||||
name: i18n.translate('kbn.server.tutorials.traefikLogs.nameTitle', {
|
||||
defaultMessage: 'Traefik logs',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.LOGGING,
|
||||
shortDescription: 'Collect and parse access logs created by the Traefik Proxy.',
|
||||
longDescription: 'The `traefik` Filebeat module parses access logs created by Traefik.' +
|
||||
' [Learn more]({config.docs.beats.filebeat}/filebeat-module-traefik.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.traefikLogs.shortDescription', {
|
||||
defaultMessage: 'Collect and parse access logs created by the Traefik Proxy.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.traefikLogs.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `traefik` Filebeat module parses access logs created by Traefik. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-traefik.html',
|
||||
},
|
||||
}),
|
||||
//euiIconType: 'logoTraefik',
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: 'Filebeat-Traefik-Dashboard',
|
||||
linkLabel: 'Traefik logs dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.traefikLogs.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'Traefik logs dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,18 +25,30 @@ export function uwsgiMetricsSpecProvider() {
|
|||
const moduleName = 'uwsgi';
|
||||
return {
|
||||
id: 'uwsgiMetrics',
|
||||
name: 'uWSGI metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.uwsgiMetrics.nameTitle', {
|
||||
defaultMessage: 'uWSGI metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from the uWSGI server.',
|
||||
longDescription: 'The `uwsgi` Metricbeat module fetches internal metrics from the uWSGI server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-uwsgi.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.uwsgiMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from the uWSGI server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.uwsgiMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `uwsgi` Metricbeat module fetches internal metrics from the uWSGI server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-uwsgi.html',
|
||||
},
|
||||
}),
|
||||
//euiIconType: 'logouWSGI',
|
||||
isBeta: true,
|
||||
artifacts: {
|
||||
dashboards: [
|
||||
{
|
||||
id: '32fca290-f0af-11e7-b9ff-9f96241065de',
|
||||
linkLabel: 'uWSGI metrics dashboard',
|
||||
linkLabel: i18n.translate('kbn.server.tutorials.uwsgiMetrics.artifacts.dashboards.linkLabel', {
|
||||
defaultMessage: 'uWSGI metrics dashboard',
|
||||
}),
|
||||
isOverview: true
|
||||
}
|
||||
],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function vSphereMetricsSpecProvider() {
|
|||
const moduleName = 'vsphere';
|
||||
return {
|
||||
id: 'vsphereMetrics',
|
||||
name: 'vSphere metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.vsphereMetrics.nameTitle', {
|
||||
defaultMessage: 'vSphere metrics',
|
||||
}),
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from vSphere.',
|
||||
longDescription: 'The `vsphere` Metricbeat module fetches internal metrics from a vSphere cluster.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-vsphere.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.vsphereMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from vSphere.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.vsphereMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `vsphere` Metricbeat module fetches internal metrics from a vSphere cluster. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-vsphere.html',
|
||||
},
|
||||
}),
|
||||
//euiIconType: 'logoVSphere',
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.vsphereMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,27 @@ export function windowsMetricsSpecProvider() {
|
|||
const moduleName = 'windows';
|
||||
return {
|
||||
id: 'windowsMetrics',
|
||||
name: 'Windows metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.windowsMetrics.nameTitle', {
|
||||
defaultMessage: 'Windows metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch internal metrics from Windows.',
|
||||
longDescription: 'The `windows` Metricbeat module fetches internal metrics from Windows.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-windows.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.windowsMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch internal metrics from Windows.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.windowsMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `windows` Metricbeat module fetches internal metrics from Windows. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-windows.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.windowsMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category';
|
||||
import { onPremInstructions, cloudInstructions, onPremCloudInstructions } from '../../../common/tutorials/metricbeat_instructions';
|
||||
|
||||
|
@ -24,15 +25,28 @@ export function zookeeperMetricsSpecProvider() {
|
|||
const moduleName = 'zookeeper';
|
||||
return {
|
||||
id: moduleName + 'Metrics',
|
||||
name: 'Zookeeper metrics',
|
||||
name: i18n.translate('kbn.server.tutorials.zookeeperMetrics.nameTitle', {
|
||||
defaultMessage: 'Zookeeper metrics',
|
||||
}),
|
||||
isBeta: true,
|
||||
category: TUTORIAL_CATEGORY.METRICS,
|
||||
shortDescription: 'Fetch interal metrics from a Zookeeper server.',
|
||||
longDescription: 'The `' + moduleName + '` Metricbeat module fetches internal metrics from a Zookeeper server.' +
|
||||
' [Learn more]({config.docs.beats.metricbeat}/metricbeat-module-' + moduleName + '.html).',
|
||||
shortDescription: i18n.translate('kbn.server.tutorials.zookeeperMetrics.shortDescription', {
|
||||
defaultMessage: 'Fetch interal metrics from a Zookeeper server.',
|
||||
}),
|
||||
longDescription: i18n.translate('kbn.server.tutorials.zookeeperMetrics.longDescription', {
|
||||
// eslint-disable-next-line no-multi-str
|
||||
defaultMessage: 'The `{moduleName}` Metricbeat module fetches internal metrics from a Zookeeper server. \
|
||||
[Learn more]({learnMoreLink}).',
|
||||
values: {
|
||||
moduleName,
|
||||
learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-' + moduleName + '.html',
|
||||
},
|
||||
}),
|
||||
artifacts: {
|
||||
application: {
|
||||
label: 'Discover',
|
||||
label: i18n.translate('kbn.server.tutorials.zookeeperMetrics.artifacts.application.label', {
|
||||
defaultMessage: 'Discover',
|
||||
}),
|
||||
path: '/app/kibana#/discover'
|
||||
},
|
||||
dashboards: [],
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { notify } from 'ui/notify';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
// Module-level error returned by notify.error
|
||||
|
@ -130,7 +130,7 @@ async function loadStatus(fetchFn = fetchData) {
|
|||
},
|
||||
);
|
||||
|
||||
errorNotif = notify.error(serverIsDownErrorMessage);
|
||||
errorNotif = toastNotifications.addDanger(serverIsDownErrorMessage);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ async function loadStatus(fetchFn = fetchData) {
|
|||
},
|
||||
);
|
||||
|
||||
errorNotif = notify.error(serverStatusCodeErrorMessage);
|
||||
errorNotif = toastNotifications.addDanger(serverStatusCodeErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import fixtures from 'fixtures/fake_hierarchical_data';
|
|||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { VisProvider } from '../../../vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { BuildHierarchicalDataProvider } from '../build_hierarchical_data';
|
||||
|
@ -276,6 +277,9 @@ describe('buildHierarchicalData', function () {
|
|||
let results;
|
||||
|
||||
beforeEach(function () {
|
||||
// Clear existing toasts.
|
||||
toastNotifications.list.splice(0);
|
||||
|
||||
let id = 1;
|
||||
vis = new Vis(indexPattern, {
|
||||
type: 'pie',
|
||||
|
@ -299,10 +303,11 @@ describe('buildHierarchicalData', function () {
|
|||
});
|
||||
|
||||
it('should set the hits attribute for the results', function () {
|
||||
const errCall = Notifier.prototype.error.getCall(0);
|
||||
expect(errCall).to.be.ok();
|
||||
expect(errCall.args[0]).to.contain('not supported');
|
||||
|
||||
// Ideally, buildHierarchicalData shouldn't be tightly coupled to toastNotifications. Instead,
|
||||
// it should notify its consumer of this error and the consumer should be responsible for
|
||||
// notifying the user. This test verifies the side effect of the error until we can remove
|
||||
// this coupling.
|
||||
expect(toastNotifications.list).to.have.length(1);
|
||||
expect(results).to.have.property('slices');
|
||||
expect(results).to.have.property('names');
|
||||
expect(results.names).to.have.length(2);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { extractBuckets } from './_extract_buckets';
|
||||
import { createRawData } from './_create_raw_data';
|
||||
import { arrayToLinkedList } from './_array_to_linked_list';
|
||||
|
@ -25,15 +26,10 @@ import AggConfigResult from '../../vis/agg_config_result';
|
|||
import { AggResponseHierarchicalBuildSplitProvider } from './_build_split';
|
||||
import { HierarchicalTooltipFormatterProvider } from './_hierarchical_tooltip_formatter';
|
||||
|
||||
export function BuildHierarchicalDataProvider(Private, Notifier) {
|
||||
export function BuildHierarchicalDataProvider(Private) {
|
||||
const buildSplit = Private(AggResponseHierarchicalBuildSplitProvider);
|
||||
const tooltipFormatter = Private(HierarchicalTooltipFormatterProvider);
|
||||
|
||||
|
||||
const notify = new Notifier({
|
||||
location: 'Pie chart response converter'
|
||||
});
|
||||
|
||||
return function (vis, resp) {
|
||||
// Create a reference to the buckets
|
||||
let buckets = vis.getAggConfig().bySchemaGroup.buckets;
|
||||
|
@ -73,7 +69,10 @@ export function BuildHierarchicalDataProvider(Private, Notifier) {
|
|||
const aggData = resp.aggregations ? resp.aggregations[firstAgg.id] : null;
|
||||
|
||||
if (!firstAgg._next && firstAgg.schema.name === 'split') {
|
||||
notify.error('Splitting charts without splitting slices is not supported. Pretending that we are just splitting slices.');
|
||||
toastNotifications.addDanger({
|
||||
title: 'Splitting charts without splitting slices is not supported',
|
||||
text: 'Pretending that we are just splitting slices.'
|
||||
});
|
||||
}
|
||||
|
||||
// start with splitting slices
|
||||
|
|
|
@ -18,9 +18,7 @@
|
|||
*/
|
||||
|
||||
import chrome from '../chrome';
|
||||
import { Notifier } from '../notify';
|
||||
|
||||
const notify = new Notifier({ location: 'Scripting Language Service' });
|
||||
import { toastNotifications } from '../notify';
|
||||
|
||||
export function getSupportedScriptingLanguages() {
|
||||
return ['painless'];
|
||||
|
@ -35,7 +33,7 @@ export function GetEnabledScriptingLanguagesProvider($http) {
|
|||
return $http.get(chrome.addBasePath('/api/kibana/scripts/languages'))
|
||||
.then((res) => res.data)
|
||||
.catch(() => {
|
||||
notify.error('Error getting available scripting languages from Elasticsearch');
|
||||
toastNotifications.addDanger('Error getting available scripting languages from Elasticsearch');
|
||||
return [];
|
||||
});
|
||||
};
|
||||
|
|
|
@ -22,5 +22,6 @@ import { dirname } from 'path';
|
|||
export const pkg = {
|
||||
__filename: require.resolve('../../package.json'),
|
||||
__dirname: dirname(require.resolve('../../package.json')),
|
||||
...require('../../package.json')
|
||||
// tslint:disable no-var-requires
|
||||
...require('../../package.json'),
|
||||
};
|
|
@ -21,6 +21,7 @@ import expect from 'expect.js';
|
|||
|
||||
export default function ({ getService, getPageObjects }) {
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
const PageObjects = getPageObjects(['common', 'home', 'settings']);
|
||||
|
||||
describe('test large number of fields @skipcloud', function () {
|
||||
|
@ -34,6 +35,8 @@ export default function ({ getService, getPageObjects }) {
|
|||
|
||||
it('test_huge data should have expected number of fields', async function () {
|
||||
const tabCount = await PageObjects.settings.getFieldsTabCount();
|
||||
//default : maxPayloadBytes is 1048576
|
||||
log.info('if there is a failure, start the server with "node scripts/functional_tests_server -- --server.maxPayloadBytes=1648576"');
|
||||
expect(tabCount).to.be(EXPECTED_FIELD_COUNT);
|
||||
});
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
const renderable = getService('renderable');
|
||||
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
|
||||
|
||||
describe('data table with index without time filter', function indexPatternCreation() {
|
||||
describe.skip('data table with index without time filter', function indexPatternCreation() {
|
||||
const vizName1 = 'Visualization DataTable without time filter';
|
||||
|
||||
before(async function () {
|
||||
|
|
|
@ -24,7 +24,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
const retry = getService('retry');
|
||||
const PageObjects = getPageObjects(['common', 'visualize', 'header']);
|
||||
|
||||
describe('vertical bar chart with index without time filter', function () {
|
||||
describe.skip('vertical bar chart with index without time filter', function () {
|
||||
const vizName1 = 'Visualization VerticalBarChart without time filter';
|
||||
|
||||
const initBarChart = async () => {
|
||||
|
|
|
@ -24,6 +24,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
|
|||
const testSubjects = getService('testSubjects');
|
||||
const flyout = getService('flyout');
|
||||
const PageObjects = getPageObjects(['header', 'common']);
|
||||
const find = getService('find');
|
||||
|
||||
return new class DashboardAddPanel {
|
||||
async clickOpenAddPanel() {
|
||||
|
@ -94,8 +95,10 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
|
|||
}
|
||||
|
||||
async waitForEuiTableLoading() {
|
||||
const addPanel = await testSubjects.find('dashboardAddPanel');
|
||||
await addPanel.waitForDeletedByClassName('euiBasicTable-loading');
|
||||
await retry.waitFor('dashboard add panel loading to complete', async () => {
|
||||
const table = await find.byClassName('euiBasicTable');
|
||||
return !((await table.getAttribute('class')).includes('loading'));
|
||||
});
|
||||
}
|
||||
|
||||
async closeAddPanel() {
|
||||
|
@ -169,8 +172,6 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) {
|
|||
async addVisualization(vizName) {
|
||||
log.debug(`DashboardAddPanel.addVisualization(${vizName})`);
|
||||
await this.ensureAddPanelIsShowing();
|
||||
// workaround for timing issue with slideout animation
|
||||
await PageObjects.common.sleep(500);
|
||||
await this.filterEmbeddableNames(`"${vizName.replace('-', ' ')}"`);
|
||||
await testSubjects.click(`addPanel${vizName.split(' ').join('-')}`);
|
||||
await this.closeAddPanel();
|
||||
|
|
|
@ -72,6 +72,13 @@ export function FindProvider({ getService }) {
|
|||
});
|
||||
}
|
||||
|
||||
async byClassName(selector, timeout = defaultFindTimeout) {
|
||||
log.debug(`findByCssSelector ${selector}`);
|
||||
return await this._ensureElementWithTimeout(timeout, async remote => {
|
||||
return await remote.findByClassName(selector);
|
||||
});
|
||||
}
|
||||
|
||||
async setValue(selector, text) {
|
||||
return await retry.try(async () => {
|
||||
const element = await this.byCssSelector(selector);
|
||||
|
|
|
@ -101,7 +101,7 @@ uiRoutes
|
|||
return savedGraphWorkspaces.get($route.current.params.id)
|
||||
.catch(
|
||||
function () {
|
||||
notify.error('Missing workspace');
|
||||
toastNotifications.addDanger('Missing workspace');
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -830,7 +830,7 @@ app.controller('graphuiPlugin', function ($scope, $route, $interval, $http, kbnU
|
|||
}
|
||||
});
|
||||
if(!savedObjectIndexPattern) {
|
||||
notify.error('Missing index pattern:' + wsObj.indexPattern);
|
||||
toastNotifications.addDanger(`'Missing index pattern ${wsObj.indexPattern}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,24 @@ const HEADERS = {
|
|||
};
|
||||
|
||||
export class IndexTableUi extends Component {
|
||||
static getDerivedStateFromProps(props, state) {
|
||||
// Deselct any indices which no longer exist, e.g. they've been deleted.
|
||||
const { selectedIndicesMap } = state;
|
||||
const indexNames = props.indices.map(index => index.name);
|
||||
const selectedIndexNames = Object.keys(selectedIndicesMap);
|
||||
const missingIndexNames = selectedIndexNames.filter(selectedIndexName => {
|
||||
return !indexNames.includes(selectedIndexName);
|
||||
});
|
||||
|
||||
if (missingIndexNames.length) {
|
||||
const newMap = { ...selectedIndicesMap };
|
||||
missingIndexNames.forEach(missingIndexName => delete newMap[missingIndexName]);
|
||||
return { selectedIndicesMap: newMap };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -82,6 +100,7 @@ export class IndexTableUi extends Component {
|
|||
const newIsSortAscending = sortField === column ? !isSortAscending : true;
|
||||
sortChanged(column, newIsSortAscending);
|
||||
};
|
||||
|
||||
toggleAll = () => {
|
||||
const allSelected = this.areAllItemsSelected();
|
||||
if (allSelected) {
|
||||
|
@ -96,6 +115,7 @@ export class IndexTableUi extends Component {
|
|||
selectedIndicesMap
|
||||
});
|
||||
};
|
||||
|
||||
toggleItem = name => {
|
||||
this.setState(({ selectedIndicesMap }) => {
|
||||
const newMap = { ...selectedIndicesMap };
|
||||
|
@ -109,6 +129,7 @@ export class IndexTableUi extends Component {
|
|||
};
|
||||
});
|
||||
};
|
||||
|
||||
isItemSelected = name => {
|
||||
return !!this.state.selectedIndicesMap[name];
|
||||
};
|
||||
|
@ -159,6 +180,7 @@ export class IndexTableUi extends Component {
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
buildRowCells(index) {
|
||||
return Object.keys(HEADERS).map(fieldName => {
|
||||
const { name } = index;
|
||||
|
@ -174,6 +196,7 @@ export class IndexTableUi extends Component {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
buildRows() {
|
||||
const { indices = [], detailPanelIndexName } = this.props;
|
||||
return indices.map(index => {
|
||||
|
@ -221,7 +244,6 @@ export class IndexTableUi extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
|
||||
const {
|
||||
filterChanged,
|
||||
filter,
|
||||
|
@ -336,4 +358,4 @@ export class IndexTableUi extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
export const IndexTable = injectI18n(IndexTableUi);
|
||||
export const IndexTable = injectI18n(IndexTableUi);
|
||||
|
|
|
@ -65,11 +65,7 @@ export function getSettingsCollector(server) {
|
|||
|
||||
// skip everything if defaultAdminEmail === undefined
|
||||
if (defaultAdminEmail || (defaultAdminEmail === null && shouldUseNull)) {
|
||||
kibanaSettingsData = {
|
||||
xpack: {
|
||||
default_admin_email: defaultAdminEmail
|
||||
}
|
||||
};
|
||||
kibanaSettingsData = this.getEmailValueStructure(defaultAdminEmail);
|
||||
this.log.debug(`[${defaultAdminEmail}] default admin email setting found, sending [${KIBANA_SETTINGS_TYPE}] monitoring document.`);
|
||||
} else {
|
||||
this.log.debug(`not sending [${KIBANA_SETTINGS_TYPE}] monitoring document because [${defaultAdminEmail}] is null or invalid.`);
|
||||
|
@ -80,6 +76,13 @@ export function getSettingsCollector(server) {
|
|||
|
||||
// returns undefined if there was no result
|
||||
return kibanaSettingsData;
|
||||
},
|
||||
getEmailValueStructure(email) {
|
||||
return {
|
||||
xpack: {
|
||||
default_admin_email: email
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,7 +26,10 @@ export function settingsRoute(server, kbnServer) {
|
|||
const { collectorSet } = server.usage;
|
||||
const settingsCollector = collectorSet.getCollectorByType(KIBANA_SETTINGS_TYPE);
|
||||
|
||||
const settings = await settingsCollector.fetch(callCluster);
|
||||
let settings = await settingsCollector.fetch(callCluster);
|
||||
if (!settings) {
|
||||
settings = settingsCollector.getEmailValueStructure(null);
|
||||
}
|
||||
const uuid = await getClusterUuid(callCluster);
|
||||
|
||||
const kibana = getKibanaInfoForStats(server, kbnServer);
|
||||
|
|
|
@ -35,6 +35,7 @@ export default function ({ getService }) {
|
|||
expect(body.settings.kibana.transport_address.length > 0).to.eql(true);
|
||||
expect(body.settings.kibana.version.length > 0).to.eql(true);
|
||||
expect(body.settings.kibana.status.length > 0).to.eql(true);
|
||||
expect(body.settings.xpack.default_admin_email).to.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue