mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Remove /src/legacy (#95510) * starting removing stuff * fix jest config * disable CLI mode until other PR is merged * fix the schema * add deprecation for maxPayloadBytes * fix legacy start logic * deletes `env` from unknown args * fix FTR test config * some legacy service deletion * move config validation * remove legacy exports from entrypoint * preserve legacy logging in core logging config * try to fix uiSettings integration tests * fix legacy service tests * more type fix * use fromRoot from @kbn/utils * cleanup kibana.d.ts * fix unit tests * remove src/core/server/utils * fix server script * add integration test for `/{path*}` route * add unit tests on legacy config * adapt uiSetting IT bis * fix tests * update generated doc * address some review comments * move review comments * fix some stuff * fix some stuff * fix some stuff * fix some stuff bis * generated doc * add test for ensureValidConfiguration # Conflicts: # .github/CODEOWNERS # src/cli_plugin/install/core_plugins/kibana/public/context/query_parameters/state.js # src/core/server/http/http_config.ts # x-pack/test/functional/config.js * codestyle
This commit is contained in:
parent
ad7866592e
commit
248390f779
111 changed files with 743 additions and 4096 deletions
|
@ -21,19 +21,13 @@ snapshots.js
|
|||
|
||||
# plugin overrides
|
||||
/src/core/lib/kbn_internal_native_observable
|
||||
/src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken
|
||||
/src/plugins/data/common/es_query/kuery/ast/_generated_/**
|
||||
/src/plugins/vis_type_timelion/common/_generated_/**
|
||||
/x-pack/legacy/plugins/**/__tests__/fixtures/**
|
||||
/x-pack/plugins/apm/e2e/tmp/*
|
||||
/x-pack/plugins/canvas/canvas_plugin
|
||||
/x-pack/plugins/canvas/shareable_runtime/build
|
||||
/x-pack/plugins/canvas/storybook/build
|
||||
/x-pack/plugins/reporting/server/export_types/printable_pdf/server/lib/pdf/assets/**
|
||||
/x-pack/legacy/plugins/infra/common/graphql/types.ts
|
||||
/x-pack/legacy/plugins/infra/public/graphql/types.ts
|
||||
/x-pack/legacy/plugins/infra/server/graphql/types.ts
|
||||
/x-pack/legacy/plugins/maps/public/vendor/**
|
||||
|
||||
# package overrides
|
||||
/packages/elastic-eslint-config-kibana
|
||||
|
|
24
.eslintrc.js
24
.eslintrc.js
|
@ -416,11 +416,7 @@ module.exports = {
|
|||
errorMessage: `Common code can not import from server or public, use a common directory.`,
|
||||
},
|
||||
{
|
||||
target: [
|
||||
'src/legacy/**/*',
|
||||
'(src|x-pack)/plugins/**/(public|server)/**/*',
|
||||
'examples/**/*',
|
||||
],
|
||||
target: ['(src|x-pack)/plugins/**/(public|server)/**/*', 'examples/**/*'],
|
||||
from: [
|
||||
'src/core/public/**/*',
|
||||
'!src/core/public/index.ts', // relative import
|
||||
|
@ -434,8 +430,6 @@ module.exports = {
|
|||
'!src/core/server/mocks{,.ts}',
|
||||
'!src/core/server/types{,.ts}',
|
||||
'!src/core/server/test_utils{,.ts}',
|
||||
'!src/core/server/utils', // ts alias
|
||||
'!src/core/server/utils/**/*',
|
||||
// for absolute imports until fixed in
|
||||
// https://github.com/elastic/kibana/issues/36096
|
||||
'!src/core/server/*.test.mocks{,.ts}',
|
||||
|
@ -448,7 +442,6 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
target: [
|
||||
'src/legacy/**/*',
|
||||
'(src|x-pack)/plugins/**/(public|server)/**/*',
|
||||
'examples/**/*',
|
||||
'!(src|x-pack)/**/*.test.*',
|
||||
|
@ -486,7 +479,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
target: ['src/core/**/*'],
|
||||
from: ['plugins/**/*', 'src/plugins/**/*', 'src/legacy/ui/**/*'],
|
||||
from: ['plugins/**/*', 'src/plugins/**/*'],
|
||||
errorMessage: 'The core cannot depend on any plugins.',
|
||||
},
|
||||
{
|
||||
|
@ -494,19 +487,6 @@ module.exports = {
|
|||
from: ['ui/**/*'],
|
||||
errorMessage: 'Plugins cannot import legacy UI code.',
|
||||
},
|
||||
{
|
||||
from: ['src/legacy/ui/**/*', 'ui/**/*'],
|
||||
target: [
|
||||
'test/plugin_functional/plugins/**/public/np_ready/**/*',
|
||||
'test/plugin_functional/plugins/**/server/np_ready/**/*',
|
||||
],
|
||||
allowSameFolder: true,
|
||||
errorMessage:
|
||||
'NP-ready code should not import from /src/legacy/ui/** folder. ' +
|
||||
'Instead of importing from /src/legacy/ui/** deeply within a np_ready folder, ' +
|
||||
'import those things once at the top level of your plugin and pass those down, just ' +
|
||||
'like you pass down `core` and `plugins` objects.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -10,10 +10,10 @@ Set of helpers used to create `KibanaResponse` to form HTTP response on an incom
|
|||
|
||||
```typescript
|
||||
kibanaResponseFactory: {
|
||||
custom: <T extends string | Record<string, any> | Error | Buffer | {
|
||||
custom: <T extends string | Record<string, any> | Error | Buffer | Stream | {
|
||||
message: string | Error;
|
||||
attributes?: Record<string, any> | undefined;
|
||||
} | Stream | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||
} | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) > [core](./kibana-plugin-core-server.legacyservicesetupdeps.core.md)
|
||||
|
||||
## LegacyServiceSetupDeps.core property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
core: LegacyCoreSetup;
|
||||
```
|
|
@ -1,24 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md)
|
||||
|
||||
## LegacyServiceSetupDeps interface
|
||||
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface LegacyServiceSetupDeps
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [core](./kibana-plugin-core-server.legacyservicesetupdeps.core.md) | <code>LegacyCoreSetup</code> | |
|
||||
| [plugins](./kibana-plugin-core-server.legacyservicesetupdeps.plugins.md) | <code>Record<string, unknown></code> | |
|
||||
| [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md) | <code>UiPlugins</code> | |
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) > [plugins](./kibana-plugin-core-server.legacyservicesetupdeps.plugins.md)
|
||||
|
||||
## LegacyServiceSetupDeps.plugins property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
plugins: Record<string, unknown>;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) > [uiPlugins](./kibana-plugin-core-server.legacyservicesetupdeps.uiplugins.md)
|
||||
|
||||
## LegacyServiceSetupDeps.uiPlugins property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
uiPlugins: UiPlugins;
|
||||
```
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) > [core](./kibana-plugin-core-server.legacyservicestartdeps.core.md)
|
||||
|
||||
## LegacyServiceStartDeps.core property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
core: LegacyCoreStart;
|
||||
```
|
|
@ -1,23 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md)
|
||||
|
||||
## LegacyServiceStartDeps interface
|
||||
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface LegacyServiceStartDeps
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [core](./kibana-plugin-core-server.legacyservicestartdeps.core.md) | <code>LegacyCoreStart</code> | |
|
||||
| [plugins](./kibana-plugin-core-server.legacyservicestartdeps.plugins.md) | <code>Record<string, unknown></code> | |
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) > [plugins](./kibana-plugin-core-server.legacyservicestartdeps.plugins.md)
|
||||
|
||||
## LegacyServiceStartDeps.plugins property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
plugins: Record<string, unknown>;
|
||||
```
|
|
@ -110,8 +110,6 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [LegacyCallAPIOptions](./kibana-plugin-core-server.legacycallapioptions.md) | The set of options that defines how API call should be made and result be processed. |
|
||||
| [LegacyElasticsearchError](./kibana-plugin-core-server.legacyelasticsearcherror.md) | @<!-- -->deprecated. The new elasticsearch client doesn't wrap errors anymore. |
|
||||
| [LegacyRequest](./kibana-plugin-core-server.legacyrequest.md) | |
|
||||
| [LegacyServiceSetupDeps](./kibana-plugin-core-server.legacyservicesetupdeps.md) | |
|
||||
| [LegacyServiceStartDeps](./kibana-plugin-core-server.legacyservicestartdeps.md) | |
|
||||
| [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | |
|
||||
| [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. |
|
||||
| [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. |
|
||||
|
|
|
@ -12,7 +12,7 @@ start(core: CoreStart): {
|
|||
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
|
||||
};
|
||||
indexPatterns: {
|
||||
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("../../../core/server").SavedObjectsClient, "get" | "delete" | "create" | "bulkCreate" | "checkConflicts" | "find" | "bulkGet" | "resolve" | "update" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo" | "openPointInTimeForType" | "closePointInTime" | "createPointInTimeFinder" | "errors">, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise<import(".").IndexPatternsService>;
|
||||
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("../../../core/server").SavedObjectsClient, "update" | "get" | "delete" | "create" | "bulkCreate" | "checkConflicts" | "find" | "bulkGet" | "resolve" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo" | "openPointInTimeForType" | "closePointInTime" | "createPointInTimeFinder" | "errors">, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise<import(".").IndexPatternsService>;
|
||||
};
|
||||
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
|
||||
};
|
||||
|
@ -31,7 +31,7 @@ start(core: CoreStart): {
|
|||
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
|
||||
};
|
||||
indexPatterns: {
|
||||
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("../../../core/server").SavedObjectsClient, "get" | "delete" | "create" | "bulkCreate" | "checkConflicts" | "find" | "bulkGet" | "resolve" | "update" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo" | "openPointInTimeForType" | "closePointInTime" | "createPointInTimeFinder" | "errors">, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise<import(".").IndexPatternsService>;
|
||||
indexPatternsServiceFactory: (savedObjectsClient: Pick<import("../../../core/server").SavedObjectsClient, "update" | "get" | "delete" | "create" | "bulkCreate" | "checkConflicts" | "find" | "bulkGet" | "resolve" | "addToNamespaces" | "deleteFromNamespaces" | "bulkUpdate" | "removeReferencesTo" | "openPointInTimeForType" | "closePointInTime" | "createPointInTimeFinder" | "errors">, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise<import(".").IndexPatternsService>;
|
||||
};
|
||||
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
|
||||
}`
|
||||
|
|
|
@ -12,7 +12,6 @@ module.exports = {
|
|||
projects: [
|
||||
'<rootDir>/packages/*/jest.config.js',
|
||||
'<rootDir>/src/*/jest.config.js',
|
||||
'<rootDir>/src/legacy/*/jest.config.js',
|
||||
'<rootDir>/src/plugins/*/jest.config.js',
|
||||
'<rootDir>/test/*/jest.config.js',
|
||||
'<rootDir>/x-pack/plugins/*/jest.config.js',
|
||||
|
|
15
kibana.d.ts
vendored
15
kibana.d.ts
vendored
|
@ -13,18 +13,3 @@ import * as Public from 'src/core/public';
|
|||
import * as Server from 'src/core/server';
|
||||
|
||||
export { Public, Server };
|
||||
|
||||
/**
|
||||
* All exports from TS ambient definitions (where types are added for JS source in a .d.ts file).
|
||||
*/
|
||||
import * as LegacyKibanaServer from './src/legacy/server/kbn_server';
|
||||
|
||||
/**
|
||||
* Re-export legacy types under a namespace.
|
||||
*/
|
||||
export namespace Legacy {
|
||||
export type KibanaConfig = LegacyKibanaServer.KibanaConfig;
|
||||
export type Request = LegacyKibanaServer.Request;
|
||||
export type ResponseToolkit = LegacyKibanaServer.ResponseToolkit;
|
||||
export type Server = LegacyKibanaServer.Server;
|
||||
}
|
||||
|
|
|
@ -27,8 +27,6 @@ it('produces the right watch and ignore list', () => {
|
|||
expect(watchPaths).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
<absolute path>/src/core,
|
||||
<absolute path>/src/legacy/server,
|
||||
<absolute path>/src/legacy/utils,
|
||||
<absolute path>/config,
|
||||
<absolute path>/x-pack/test/plugin_functional/plugins/resolver_test,
|
||||
<absolute path>/src/plugins,
|
||||
|
|
|
@ -1,69 +1,5 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`#get correctly handles server config.: default 1`] = `
|
||||
Object {
|
||||
"autoListen": true,
|
||||
"basePath": "/abc",
|
||||
"compression": Object {
|
||||
"enabled": true,
|
||||
},
|
||||
"cors": false,
|
||||
"customResponseHeaders": Object {
|
||||
"custom-header": "custom-value",
|
||||
},
|
||||
"host": "host",
|
||||
"keepaliveTimeout": 5000,
|
||||
"maxPayload": 1000,
|
||||
"name": "kibana-hostname",
|
||||
"port": 1234,
|
||||
"publicBaseUrl": "https://myhost.com/abc",
|
||||
"rewriteBasePath": false,
|
||||
"socketTimeout": 2000,
|
||||
"ssl": Object {
|
||||
"enabled": true,
|
||||
"keyPassphrase": "some-phrase",
|
||||
"someNewValue": "new",
|
||||
},
|
||||
"uuid": undefined,
|
||||
"xsrf": Object {
|
||||
"allowlist": Array [],
|
||||
"disableProtection": false,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#get correctly handles server config.: disabled ssl 1`] = `
|
||||
Object {
|
||||
"autoListen": true,
|
||||
"basePath": "/abc",
|
||||
"compression": Object {
|
||||
"enabled": true,
|
||||
},
|
||||
"cors": false,
|
||||
"customResponseHeaders": Object {
|
||||
"custom-header": "custom-value",
|
||||
},
|
||||
"host": "host",
|
||||
"keepaliveTimeout": 5000,
|
||||
"maxPayload": 1000,
|
||||
"name": "kibana-hostname",
|
||||
"port": 1234,
|
||||
"publicBaseUrl": "http://myhost.com/abc",
|
||||
"rewriteBasePath": false,
|
||||
"socketTimeout": 2000,
|
||||
"ssl": Object {
|
||||
"certificate": "cert",
|
||||
"enabled": false,
|
||||
"key": "key",
|
||||
},
|
||||
"uuid": undefined,
|
||||
"xsrf": Object {
|
||||
"allowlist": Array [],
|
||||
"disableProtection": false,
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`#get correctly handles silent logging config. 1`] = `
|
||||
Object {
|
||||
"appenders": Object {
|
||||
|
@ -78,6 +14,7 @@ Object {
|
|||
"root": Object {
|
||||
"level": "off",
|
||||
},
|
||||
"silent": true,
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -93,10 +30,13 @@ Object {
|
|||
"type": "legacy-appender",
|
||||
},
|
||||
},
|
||||
"dest": "/some/path.log",
|
||||
"json": true,
|
||||
"loggers": undefined,
|
||||
"root": Object {
|
||||
"level": "all",
|
||||
},
|
||||
"verbose": true,
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -65,59 +65,6 @@ describe('#get', () => {
|
|||
|
||||
expect(configAdapter.get('logging')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('correctly handles server config.', () => {
|
||||
const configAdapter = new LegacyObjectToConfigAdapter({
|
||||
server: {
|
||||
name: 'kibana-hostname',
|
||||
autoListen: true,
|
||||
basePath: '/abc',
|
||||
cors: false,
|
||||
customResponseHeaders: { 'custom-header': 'custom-value' },
|
||||
host: 'host',
|
||||
maxPayloadBytes: 1000,
|
||||
keepaliveTimeout: 5000,
|
||||
socketTimeout: 2000,
|
||||
port: 1234,
|
||||
publicBaseUrl: 'https://myhost.com/abc',
|
||||
rewriteBasePath: false,
|
||||
ssl: { enabled: true, keyPassphrase: 'some-phrase', someNewValue: 'new' },
|
||||
compression: { enabled: true },
|
||||
someNotSupportedValue: 'val',
|
||||
xsrf: {
|
||||
disableProtection: false,
|
||||
allowlist: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const configAdapterWithDisabledSSL = new LegacyObjectToConfigAdapter({
|
||||
server: {
|
||||
name: 'kibana-hostname',
|
||||
autoListen: true,
|
||||
basePath: '/abc',
|
||||
cors: false,
|
||||
customResponseHeaders: { 'custom-header': 'custom-value' },
|
||||
host: 'host',
|
||||
maxPayloadBytes: 1000,
|
||||
keepaliveTimeout: 5000,
|
||||
socketTimeout: 2000,
|
||||
port: 1234,
|
||||
publicBaseUrl: 'http://myhost.com/abc',
|
||||
rewriteBasePath: false,
|
||||
ssl: { enabled: false, certificate: 'cert', key: 'key' },
|
||||
compression: { enabled: true },
|
||||
someNotSupportedValue: 'val',
|
||||
xsrf: {
|
||||
disableProtection: false,
|
||||
allowlist: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(configAdapter.get('server')).toMatchSnapshot('default');
|
||||
expect(configAdapterWithDisabledSSL.get('server')).toMatchSnapshot('disabled ssl');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set', () => {
|
||||
|
|
|
@ -9,15 +9,6 @@
|
|||
import { ConfigPath } from '../config';
|
||||
import { ObjectToConfigAdapter } from '../object_to_config_adapter';
|
||||
|
||||
// TODO: fix once core schemas are moved to this package
|
||||
type LoggingConfigType = any;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export type LegacyVars = Record<string, any>;
|
||||
|
||||
/**
|
||||
* Represents logging config supported by the legacy platform.
|
||||
*/
|
||||
|
@ -30,7 +21,7 @@ export interface LegacyLoggingConfig {
|
|||
events?: Record<string, string>;
|
||||
}
|
||||
|
||||
type MixedLoggingConfig = LegacyLoggingConfig & Partial<LoggingConfigType>;
|
||||
type MixedLoggingConfig = LegacyLoggingConfig & Record<string, any>;
|
||||
|
||||
/**
|
||||
* Represents adapter between config provided by legacy platform and `Config`
|
||||
|
@ -48,6 +39,7 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter {
|
|||
},
|
||||
root: { level: 'info', ...root },
|
||||
loggers,
|
||||
...legacyLoggingConfig,
|
||||
};
|
||||
|
||||
if (configValue.silent) {
|
||||
|
@ -61,47 +53,11 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter {
|
|||
return loggingConfig;
|
||||
}
|
||||
|
||||
private static transformServer(configValue: any = {}) {
|
||||
// TODO: New platform uses just a subset of `server` config from the legacy platform,
|
||||
// new values will be exposed once we need them
|
||||
return {
|
||||
autoListen: configValue.autoListen,
|
||||
basePath: configValue.basePath,
|
||||
cors: configValue.cors,
|
||||
customResponseHeaders: configValue.customResponseHeaders,
|
||||
host: configValue.host,
|
||||
maxPayload: configValue.maxPayloadBytes,
|
||||
name: configValue.name,
|
||||
port: configValue.port,
|
||||
publicBaseUrl: configValue.publicBaseUrl,
|
||||
rewriteBasePath: configValue.rewriteBasePath,
|
||||
ssl: configValue.ssl,
|
||||
keepaliveTimeout: configValue.keepaliveTimeout,
|
||||
socketTimeout: configValue.socketTimeout,
|
||||
compression: configValue.compression,
|
||||
uuid: configValue.uuid,
|
||||
xsrf: configValue.xsrf,
|
||||
};
|
||||
}
|
||||
|
||||
private static transformPlugins(configValue: LegacyVars = {}) {
|
||||
// These properties are the only ones we use from the existing `plugins` config node
|
||||
// since `scanDirs` isn't respected by new platform plugin discovery.
|
||||
return {
|
||||
initialize: configValue.initialize,
|
||||
paths: configValue.paths,
|
||||
};
|
||||
}
|
||||
|
||||
public get(configPath: ConfigPath) {
|
||||
const configValue = super.get(configPath);
|
||||
switch (configPath) {
|
||||
case 'logging':
|
||||
return LegacyObjectToConfigAdapter.transformLogging(configValue as LegacyLoggingConfig);
|
||||
case 'server':
|
||||
return LegacyObjectToConfigAdapter.transformServer(configValue);
|
||||
case 'plugins':
|
||||
return LegacyObjectToConfigAdapter.transformPlugins(configValue as LegacyVars);
|
||||
default:
|
||||
return configValue;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"kbn:watch": "yarn build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kbn/utils": "link:../kbn-utils"
|
||||
"@kbn/utils": "link:../kbn-utils",
|
||||
"@kbn/config-schema": "link:../kbn-config-schema"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ export class LegacyLoggingServer {
|
|||
// We set `ops.interval` to max allowed number and `ops` filter to value
|
||||
// that doesn't exist to avoid logging of ops at all, if turned on it will be
|
||||
// logged by the "legacy" Kibana.
|
||||
const { value: loggingConfig } = legacyLoggingConfigSchema.validate({
|
||||
const loggingConfig = legacyLoggingConfigSchema.validate({
|
||||
...legacyLoggingConfig,
|
||||
events: {
|
||||
...legacyLoggingConfig.events,
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const HANDLED_IN_KIBANA_PLATFORM = Joi.any().description(
|
||||
'This key is handled in the new platform ONLY'
|
||||
);
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
|
@ -36,46 +33,65 @@ export interface LegacyLoggingConfig {
|
|||
};
|
||||
}
|
||||
|
||||
export const legacyLoggingConfigSchema = Joi.object()
|
||||
.keys({
|
||||
appenders: HANDLED_IN_KIBANA_PLATFORM,
|
||||
loggers: HANDLED_IN_KIBANA_PLATFORM,
|
||||
root: HANDLED_IN_KIBANA_PLATFORM,
|
||||
|
||||
silent: Joi.boolean().default(false),
|
||||
quiet: Joi.boolean().when('silent', {
|
||||
is: true,
|
||||
then: Joi.boolean().default(true).valid(true),
|
||||
otherwise: Joi.boolean().default(false),
|
||||
export const legacyLoggingConfigSchema = schema.object({
|
||||
silent: schema.boolean({ defaultValue: false }),
|
||||
quiet: schema.conditional(
|
||||
schema.siblingRef('silent'),
|
||||
true,
|
||||
schema.boolean({
|
||||
defaultValue: true,
|
||||
validate: (quiet) => {
|
||||
if (!quiet) {
|
||||
return 'must be true when `silent` is true';
|
||||
}
|
||||
},
|
||||
}),
|
||||
verbose: Joi.boolean().when('quiet', {
|
||||
is: true,
|
||||
then: Joi.valid(false).default(false),
|
||||
otherwise: Joi.boolean().default(false),
|
||||
schema.boolean({ defaultValue: false })
|
||||
),
|
||||
verbose: schema.conditional(
|
||||
schema.siblingRef('quiet'),
|
||||
true,
|
||||
schema.boolean({
|
||||
defaultValue: false,
|
||||
validate: (verbose) => {
|
||||
if (verbose) {
|
||||
return 'must be false when `quiet` is true';
|
||||
}
|
||||
},
|
||||
}),
|
||||
events: Joi.any().default({}),
|
||||
dest: Joi.string().default('stdout'),
|
||||
filter: Joi.any().default({}),
|
||||
json: Joi.boolean().when('dest', {
|
||||
is: 'stdout',
|
||||
then: Joi.boolean().default(!process.stdout.isTTY),
|
||||
otherwise: Joi.boolean().default(true),
|
||||
schema.boolean({ defaultValue: false })
|
||||
),
|
||||
events: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
|
||||
dest: schema.string({ defaultValue: 'stdout' }),
|
||||
filter: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
|
||||
json: schema.conditional(
|
||||
schema.siblingRef('dest'),
|
||||
'stdout',
|
||||
schema.boolean({
|
||||
defaultValue: !process.stdout.isTTY,
|
||||
}),
|
||||
timezone: Joi.string(),
|
||||
rotate: Joi.object()
|
||||
.keys({
|
||||
enabled: Joi.boolean().default(false),
|
||||
everyBytes: Joi.number()
|
||||
// > 1MB
|
||||
.greater(1048576)
|
||||
// < 1GB
|
||||
.less(1073741825)
|
||||
// 10MB
|
||||
.default(10485760),
|
||||
keepFiles: Joi.number().greater(2).less(1024).default(7),
|
||||
pollingInterval: Joi.number().greater(5000).less(3600000).default(10000),
|
||||
usePolling: Joi.boolean().default(false),
|
||||
})
|
||||
.default(),
|
||||
})
|
||||
.default();
|
||||
schema.boolean({
|
||||
defaultValue: true,
|
||||
})
|
||||
),
|
||||
timezone: schema.maybe(schema.string()),
|
||||
rotate: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
everyBytes: schema.number({
|
||||
min: 1048576, // > 1MB
|
||||
max: 1073741825, // < 1GB
|
||||
defaultValue: 10485760, // 10MB
|
||||
}),
|
||||
keepFiles: schema.number({
|
||||
min: 2,
|
||||
max: 1024,
|
||||
defaultValue: 7,
|
||||
}),
|
||||
pollingInterval: schema.number({
|
||||
min: 5000,
|
||||
max: 3600000,
|
||||
defaultValue: 10000,
|
||||
}),
|
||||
usePolling: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -14,3 +14,7 @@ export const kibanaPackageJson = {
|
|||
__dirname: dirname(resolve(REPO_ROOT, 'package.json')),
|
||||
...require(resolve(REPO_ROOT, 'package.json')),
|
||||
};
|
||||
|
||||
export const isKibanaDistributable = () => {
|
||||
return kibanaPackageJson.build && kibanaPackageJson.build.distributable === true;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { pkg } from '../core/server/utils';
|
||||
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||
import Command from './command';
|
||||
import serveCommand from './serve/serve';
|
||||
|
||||
|
|
|
@ -12,8 +12,7 @@ import { statSync } from 'fs';
|
|||
import { resolve } from 'path';
|
||||
import url from 'url';
|
||||
|
||||
import { getConfigPath, fromRoot } from '@kbn/utils';
|
||||
import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
|
||||
import { getConfigPath, fromRoot, isKibanaDistributable } from '@kbn/utils';
|
||||
import { readKeystore } from '../keystore/read_keystore';
|
||||
|
||||
function canRequire(path) {
|
||||
|
@ -65,9 +64,10 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) {
|
|||
delete rawConfig.xpack;
|
||||
}
|
||||
|
||||
if (opts.dev) {
|
||||
set('env', 'development');
|
||||
// only used to set cliArgs.envName, we don't want to inject that into the config
|
||||
delete extraCliOptions.env;
|
||||
|
||||
if (opts.dev) {
|
||||
if (!has('elasticsearch.username')) {
|
||||
set('elasticsearch.username', 'kibana_system');
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ export default function (program) {
|
|||
.option('--plugins <path>', 'an alias for --plugin-dir', pluginDirCollector)
|
||||
.option('--optimize', 'Deprecated, running the optimizer is no longer required');
|
||||
|
||||
if (!IS_KIBANA_DISTRIBUTABLE) {
|
||||
if (!isKibanaDistributable()) {
|
||||
command
|
||||
.option('--oss', 'Start Kibana without X-Pack')
|
||||
.option(
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { pkg } from '../core/server/utils';
|
||||
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||
|
||||
import Command from '../cli/command';
|
||||
import { EncryptionConfig } from './encryption_config';
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||
|
||||
import { pkg } from '../core/server/utils';
|
||||
import Command from '../cli/command';
|
||||
import { Keystore } from '../cli/keystore';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { pkg } from '../core/server/utils';
|
||||
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||
import Command from '../cli/command';
|
||||
import { listCommand } from './list';
|
||||
import { installCommand } from './install';
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getConfigPath } from '@kbn/utils';
|
||||
import { pkg } from '../../core/server/utils';
|
||||
import { getConfigPath, kibanaPackageJson as pkg } from '@kbn/utils';
|
||||
import { install } from './install';
|
||||
import { Logger } from '../lib/logger';
|
||||
import { parse, parseMilliseconds } from './settings';
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import path from 'path';
|
||||
import { statSync } from 'fs';
|
||||
|
||||
import { versionSatisfies, cleanVersion } from '../../legacy/utils/version';
|
||||
import { versionSatisfies, cleanVersion } from './utils/version';
|
||||
|
||||
export function existingInstall(settings, logger) {
|
||||
try {
|
||||
|
|
|
@ -7,10 +7,8 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
|
||||
import expiry from 'expiry-js';
|
||||
|
||||
import { fromRoot } from '../../core/server/utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
|
||||
function generateUrls({ version, plugin }) {
|
||||
return [
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
*/
|
||||
|
||||
import { createAbsolutePathSerializer } from '@kbn/dev-utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
|
||||
import { fromRoot } from '../../core/server/utils';
|
||||
import { parseMilliseconds, parse } from './settings';
|
||||
|
||||
const SECOND = 1000;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { fromRoot } from '../../core/server/utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
import { list } from './list';
|
||||
import { Logger } from '../lib/logger';
|
||||
import { logWarnings } from '../lib/log_warnings';
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
|
||||
import { fromRoot } from '../../core/server/utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
|
||||
export function parse(command, options) {
|
||||
const settings = {
|
||||
|
|
47
src/core/server/config/ensure_valid_configuration.test.ts
Normal file
47
src/core/server/config/ensure_valid_configuration.test.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { configServiceMock } from './mocks';
|
||||
import { ensureValidConfiguration } from './ensure_valid_configuration';
|
||||
import { CriticalError } from '../errors';
|
||||
|
||||
describe('ensureValidConfiguration', () => {
|
||||
let configService: ReturnType<typeof configServiceMock.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
configService = configServiceMock.create();
|
||||
configService.getUsedPaths.mockReturnValue(Promise.resolve(['core', 'elastic']));
|
||||
});
|
||||
|
||||
it('returns normally when there is no unused keys', async () => {
|
||||
configService.getUnusedPaths.mockResolvedValue([]);
|
||||
await expect(ensureValidConfiguration(configService as any)).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('throws when there are some unused keys', async () => {
|
||||
configService.getUnusedPaths.mockResolvedValue(['some.key', 'some.other.key']);
|
||||
|
||||
await expect(ensureValidConfiguration(configService as any)).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Unknown configuration key(s): "some.key", "some.other.key". Check for spelling errors and ensure that expected plugins are installed.]`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a `CriticalError` with the correct processExitCode value', async () => {
|
||||
expect.assertions(2);
|
||||
|
||||
configService.getUnusedPaths.mockResolvedValue(['some.key', 'some.other.key']);
|
||||
|
||||
try {
|
||||
await ensureValidConfiguration(configService as any);
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(CriticalError);
|
||||
expect(e.processExitCode).toEqual(64);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -6,20 +6,13 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getUnusedConfigKeys } from './get_unused_config_keys';
|
||||
import { ConfigService } from '../../config';
|
||||
import { CriticalError } from '../../errors';
|
||||
import { LegacyServiceSetupConfig } from '../types';
|
||||
import { ConfigService } from '@kbn/config';
|
||||
import { CriticalError } from '../errors';
|
||||
|
||||
export async function ensureValidConfiguration(
|
||||
configService: ConfigService,
|
||||
{ legacyConfig, settings }: LegacyServiceSetupConfig
|
||||
) {
|
||||
const unusedConfigKeys = await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: await configService.getUsedPaths(),
|
||||
settings,
|
||||
legacyConfig,
|
||||
});
|
||||
export async function ensureValidConfiguration(configService: ConfigService) {
|
||||
await configService.validate();
|
||||
|
||||
const unusedConfigKeys = await configService.getUnusedPaths();
|
||||
|
||||
if (unusedConfigKeys.length > 0) {
|
||||
const message = `Unknown configuration key(s): ${unusedConfigKeys
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
export { coreDeprecationProvider } from './deprecation';
|
||||
export { ensureValidConfiguration } from './ensure_valid_configuration';
|
||||
|
||||
export {
|
||||
ConfigService,
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
import { join } from 'path';
|
||||
import { PackageInfo } from '@kbn/config';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
import { distDir as uiSharedDepsDistDir } from '@kbn/ui-shared-deps';
|
||||
import { IRouter } from '../../http';
|
||||
import { UiPlugins } from '../../plugins';
|
||||
import { fromRoot } from '../../utils';
|
||||
import { FileHashCache } from './file_hash_cache';
|
||||
import { registerRouteForBundle } from './bundles_route';
|
||||
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
*/
|
||||
|
||||
import Path from 'path';
|
||||
import { stringify } from 'querystring';
|
||||
import { Env } from '@kbn/config';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
|
||||
import { fromRoot } from '../utils';
|
||||
import { InternalCoreSetup } from '../internal_types';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { Logger } from '../logging';
|
||||
|
@ -49,6 +51,41 @@ export class CoreApp {
|
|||
});
|
||||
});
|
||||
|
||||
// remove trailing slash catch-all
|
||||
router.get(
|
||||
{
|
||||
path: '/{path*}',
|
||||
validate: {
|
||||
params: schema.object({
|
||||
path: schema.maybe(schema.string()),
|
||||
}),
|
||||
query: schema.maybe(schema.recordOf(schema.string(), schema.any())),
|
||||
},
|
||||
},
|
||||
async (context, req, res) => {
|
||||
const { query, params } = req;
|
||||
const { path } = params;
|
||||
if (!path || !path.endsWith('/')) {
|
||||
return res.notFound();
|
||||
}
|
||||
|
||||
const basePath = httpSetup.basePath.get(req);
|
||||
let rewrittenPath = path.slice(0, -1);
|
||||
if (`/${path}`.startsWith(basePath)) {
|
||||
rewrittenPath = rewrittenPath.substring(basePath.length);
|
||||
}
|
||||
|
||||
const querystring = query ? stringify(query) : undefined;
|
||||
const url = `${basePath}/${rewrittenPath}${querystring ? `?${querystring}` : ''}`;
|
||||
|
||||
return res.redirected({
|
||||
headers: {
|
||||
location: url,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
router.get({ path: '/core', validate: false }, async (context, req, res) =>
|
||||
res.ok({ body: { version: '0.0.1' } })
|
||||
);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import * as kbnTestServer from '../../../test_helpers/kbn_server';
|
||||
import { Root } from '../../root';
|
||||
|
||||
describe('Core app routes', () => {
|
||||
let root: Root;
|
||||
|
||||
beforeAll(async function () {
|
||||
root = kbnTestServer.createRoot({
|
||||
plugins: { initialize: false },
|
||||
server: {
|
||||
basePath: '/base-path',
|
||||
},
|
||||
});
|
||||
|
||||
await root.setup();
|
||||
await root.start();
|
||||
});
|
||||
|
||||
afterAll(async function () {
|
||||
await root.shutdown();
|
||||
});
|
||||
|
||||
describe('`/{path*}` route', () => {
|
||||
it('redirects requests to include the basePath', async () => {
|
||||
const response = await kbnTestServer.request.get(root, '/some-path/').expect(302);
|
||||
expect(response.get('location')).toEqual('/base-path/some-path');
|
||||
});
|
||||
|
||||
it('includes the query in the redirect', async () => {
|
||||
const response = await kbnTestServer.request.get(root, '/some-path/?foo=bar').expect(302);
|
||||
expect(response.get('location')).toEqual('/base-path/some-path?foo=bar');
|
||||
});
|
||||
|
||||
it('does not redirect if the path does not end with `/`', async () => {
|
||||
await kbnTestServer.request.get(root, '/some-path').expect(404);
|
||||
});
|
||||
|
||||
it('does not add the basePath if the path already contains it', async () => {
|
||||
const response = await kbnTestServer.request.get(root, '/base-path/foo/').expect(302);
|
||||
expect(response.get('location')).toEqual('/base-path/foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('`/` route', () => {
|
||||
it('prevails on the `/{path*}` route', async () => {
|
||||
const response = await kbnTestServer.request.get(root, '/').expect(302);
|
||||
expect(response.get('location')).toEqual('/base-path/app/home');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,6 +11,7 @@ import { IHttpConfig, SslConfig, sslSchema } from '@kbn/server-http-tools';
|
|||
import { hostname } from 'os';
|
||||
import url from 'url';
|
||||
|
||||
import { ServiceConfigDescriptor } from '../internal_types';
|
||||
import { CspConfigType, CspConfig, ICspConfig } from '../csp';
|
||||
import { ExternalUrlConfig, IExternalUrlConfig } from '../external_url';
|
||||
|
||||
|
@ -20,136 +21,138 @@ const hostURISchema = schema.uri({ scheme: ['http', 'https'] });
|
|||
const match = (regex: RegExp, errorMsg: string) => (str: string) =>
|
||||
regex.test(str) ? undefined : errorMsg;
|
||||
|
||||
// before update to make sure it's in sync with validation rules in Legacy
|
||||
// https://github.com/elastic/kibana/blob/master/src/legacy/server/config/schema.js
|
||||
export const config = {
|
||||
path: 'server' as const,
|
||||
schema: schema.object(
|
||||
{
|
||||
name: schema.string({ defaultValue: () => hostname() }),
|
||||
autoListen: schema.boolean({ defaultValue: true }),
|
||||
publicBaseUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })),
|
||||
basePath: schema.maybe(
|
||||
schema.string({
|
||||
validate: match(validBasePathRegex, "must start with a slash, don't end with one"),
|
||||
})
|
||||
),
|
||||
cors: schema.object(
|
||||
{
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
allowCredentials: schema.boolean({ defaultValue: false }),
|
||||
allowOrigin: schema.oneOf(
|
||||
[
|
||||
schema.arrayOf(hostURISchema, { minSize: 1 }),
|
||||
schema.arrayOf(schema.literal('*'), { minSize: 1, maxSize: 1 }),
|
||||
],
|
||||
{
|
||||
defaultValue: ['*'],
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
validate(value) {
|
||||
if (value.allowCredentials === true && value.allowOrigin.includes('*')) {
|
||||
return 'Cannot specify wildcard origin "*" with "credentials: true". Please provide a list of allowed origins.';
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
customResponseHeaders: schema.recordOf(schema.string(), schema.any(), {
|
||||
defaultValue: {},
|
||||
}),
|
||||
host: schema.string({
|
||||
defaultValue: 'localhost',
|
||||
hostname: true,
|
||||
}),
|
||||
maxPayload: schema.byteSize({
|
||||
defaultValue: '1048576b',
|
||||
}),
|
||||
port: schema.number({
|
||||
defaultValue: 5601,
|
||||
}),
|
||||
rewriteBasePath: schema.boolean({ defaultValue: false }),
|
||||
ssl: sslSchema,
|
||||
keepaliveTimeout: schema.number({
|
||||
defaultValue: 120000,
|
||||
}),
|
||||
socketTimeout: schema.number({
|
||||
defaultValue: 120000,
|
||||
}),
|
||||
compression: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
referrerWhitelist: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.string({
|
||||
hostname: true,
|
||||
}),
|
||||
{ minSize: 1 }
|
||||
)
|
||||
),
|
||||
}),
|
||||
uuid: schema.maybe(
|
||||
schema.string({
|
||||
validate: match(uuidRegexp, 'must be a valid uuid'),
|
||||
})
|
||||
),
|
||||
xsrf: schema.object({
|
||||
disableProtection: schema.boolean({ defaultValue: false }),
|
||||
allowlist: schema.arrayOf(
|
||||
schema.string({ validate: match(/^\//, 'must start with a slash') }),
|
||||
{ defaultValue: [] }
|
||||
),
|
||||
}),
|
||||
requestId: schema.object(
|
||||
{
|
||||
allowFromAnyIp: schema.boolean({ defaultValue: false }),
|
||||
ipAllowlist: schema.arrayOf(schema.ip(), { defaultValue: [] }),
|
||||
},
|
||||
{
|
||||
validate(value) {
|
||||
if (value.allowFromAnyIp === true && value.ipAllowlist?.length > 0) {
|
||||
return `allowFromAnyIp must be set to 'false' if any values are specified in ipAllowlist`;
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
validate: (rawConfig) => {
|
||||
if (!rawConfig.basePath && rawConfig.rewriteBasePath) {
|
||||
return 'cannot use [rewriteBasePath] when [basePath] is not specified';
|
||||
}
|
||||
|
||||
if (rawConfig.publicBaseUrl) {
|
||||
const parsedUrl = url.parse(rawConfig.publicBaseUrl);
|
||||
if (parsedUrl.query || parsedUrl.hash || parsedUrl.auth) {
|
||||
return `[publicBaseUrl] may only contain a protocol, host, port, and pathname`;
|
||||
const configSchema = schema.object(
|
||||
{
|
||||
name: schema.string({ defaultValue: () => hostname() }),
|
||||
autoListen: schema.boolean({ defaultValue: true }),
|
||||
publicBaseUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })),
|
||||
basePath: schema.maybe(
|
||||
schema.string({
|
||||
validate: match(validBasePathRegex, "must start with a slash, don't end with one"),
|
||||
})
|
||||
),
|
||||
cors: schema.object(
|
||||
{
|
||||
enabled: schema.boolean({ defaultValue: false }),
|
||||
allowCredentials: schema.boolean({ defaultValue: false }),
|
||||
allowOrigin: schema.oneOf(
|
||||
[
|
||||
schema.arrayOf(hostURISchema, { minSize: 1 }),
|
||||
schema.arrayOf(schema.literal('*'), { minSize: 1, maxSize: 1 }),
|
||||
],
|
||||
{
|
||||
defaultValue: ['*'],
|
||||
}
|
||||
if (parsedUrl.path !== (rawConfig.basePath ?? '/')) {
|
||||
return `[publicBaseUrl] must contain the [basePath]: ${parsedUrl.path} !== ${rawConfig.basePath}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rawConfig.compression.enabled && rawConfig.compression.referrerWhitelist) {
|
||||
return 'cannot use [compression.referrerWhitelist] when [compression.enabled] is set to false';
|
||||
}
|
||||
|
||||
if (
|
||||
rawConfig.ssl.enabled &&
|
||||
rawConfig.ssl.redirectHttpFromPort !== undefined &&
|
||||
rawConfig.ssl.redirectHttpFromPort === rawConfig.port
|
||||
) {
|
||||
return (
|
||||
'Kibana does not accept http traffic to [port] when ssl is ' +
|
||||
'enabled (only https is allowed), so [ssl.redirectHttpFromPort] ' +
|
||||
`cannot be configured to the same value. Both are [${rawConfig.port}].`
|
||||
);
|
||||
}
|
||||
),
|
||||
},
|
||||
}
|
||||
),
|
||||
{
|
||||
validate(value) {
|
||||
if (value.allowCredentials === true && value.allowOrigin.includes('*')) {
|
||||
return 'Cannot specify wildcard origin "*" with "credentials: true". Please provide a list of allowed origins.';
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
customResponseHeaders: schema.recordOf(schema.string(), schema.any(), {
|
||||
defaultValue: {},
|
||||
}),
|
||||
host: schema.string({
|
||||
defaultValue: 'localhost',
|
||||
hostname: true,
|
||||
}),
|
||||
maxPayload: schema.byteSize({
|
||||
defaultValue: '1048576b',
|
||||
}),
|
||||
port: schema.number({
|
||||
defaultValue: 5601,
|
||||
}),
|
||||
rewriteBasePath: schema.boolean({ defaultValue: false }),
|
||||
ssl: sslSchema,
|
||||
keepaliveTimeout: schema.number({
|
||||
defaultValue: 120000,
|
||||
}),
|
||||
socketTimeout: schema.number({
|
||||
defaultValue: 120000,
|
||||
}),
|
||||
compression: schema.object({
|
||||
enabled: schema.boolean({ defaultValue: true }),
|
||||
referrerWhitelist: schema.maybe(
|
||||
schema.arrayOf(
|
||||
schema.string({
|
||||
hostname: true,
|
||||
}),
|
||||
{ minSize: 1 }
|
||||
)
|
||||
),
|
||||
}),
|
||||
uuid: schema.maybe(
|
||||
schema.string({
|
||||
validate: match(uuidRegexp, 'must be a valid uuid'),
|
||||
})
|
||||
),
|
||||
xsrf: schema.object({
|
||||
disableProtection: schema.boolean({ defaultValue: false }),
|
||||
allowlist: schema.arrayOf(
|
||||
schema.string({ validate: match(/^\//, 'must start with a slash') }),
|
||||
{ defaultValue: [] }
|
||||
),
|
||||
}),
|
||||
requestId: schema.object(
|
||||
{
|
||||
allowFromAnyIp: schema.boolean({ defaultValue: false }),
|
||||
ipAllowlist: schema.arrayOf(schema.ip(), { defaultValue: [] }),
|
||||
},
|
||||
{
|
||||
validate(value) {
|
||||
if (value.allowFromAnyIp === true && value.ipAllowlist?.length > 0) {
|
||||
return `allowFromAnyIp must be set to 'false' if any values are specified in ipAllowlist`;
|
||||
}
|
||||
},
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
validate: (rawConfig) => {
|
||||
if (!rawConfig.basePath && rawConfig.rewriteBasePath) {
|
||||
return 'cannot use [rewriteBasePath] when [basePath] is not specified';
|
||||
}
|
||||
|
||||
if (rawConfig.publicBaseUrl) {
|
||||
const parsedUrl = url.parse(rawConfig.publicBaseUrl);
|
||||
if (parsedUrl.query || parsedUrl.hash || parsedUrl.auth) {
|
||||
return `[publicBaseUrl] may only contain a protocol, host, port, and pathname`;
|
||||
}
|
||||
if (parsedUrl.path !== (rawConfig.basePath ?? '/')) {
|
||||
return `[publicBaseUrl] must contain the [basePath]: ${parsedUrl.path} !== ${rawConfig.basePath}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rawConfig.compression.enabled && rawConfig.compression.referrerWhitelist) {
|
||||
return 'cannot use [compression.referrerWhitelist] when [compression.enabled] is set to false';
|
||||
}
|
||||
|
||||
if (
|
||||
rawConfig.ssl.enabled &&
|
||||
rawConfig.ssl.redirectHttpFromPort !== undefined &&
|
||||
rawConfig.ssl.redirectHttpFromPort === rawConfig.port
|
||||
) {
|
||||
return (
|
||||
'Kibana does not accept http traffic to [port] when ssl is ' +
|
||||
'enabled (only https is allowed), so [ssl.redirectHttpFromPort] ' +
|
||||
`cannot be configured to the same value. Both are [${rawConfig.port}].`
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export type HttpConfigType = TypeOf<typeof configSchema>;
|
||||
|
||||
export const config: ServiceConfigDescriptor<HttpConfigType> = {
|
||||
path: 'server' as const,
|
||||
schema: configSchema,
|
||||
deprecations: ({ rename }) => [rename('maxPayloadBytes', 'maxPayload')],
|
||||
};
|
||||
export type HttpConfigType = TypeOf<typeof config.schema>;
|
||||
|
||||
export class HttpConfig implements IHttpConfig {
|
||||
public name: string;
|
||||
|
|
|
@ -12,8 +12,6 @@ import {
|
|||
legacyClusterClientInstanceMock,
|
||||
} from './core_service.test.mocks';
|
||||
|
||||
import Boom from '@hapi/boom';
|
||||
import { Request } from '@hapi/hapi';
|
||||
import { errors as esErrors } from 'elasticsearch';
|
||||
import { LegacyElasticsearchErrorHelpers } from '../../elasticsearch/legacy';
|
||||
|
||||
|
@ -22,16 +20,6 @@ import { ResponseError } from '@elastic/elasticsearch/lib/errors';
|
|||
import * as kbnTestServer from '../../../test_helpers/kbn_server';
|
||||
import { InternalElasticsearchServiceStart } from '../../elasticsearch';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
roles?: string[];
|
||||
}
|
||||
|
||||
interface StorageData {
|
||||
value: User;
|
||||
expires: number;
|
||||
}
|
||||
|
||||
const cookieOptions = {
|
||||
name: 'sid',
|
||||
encryptionKey: 'something_at_least_32_characters',
|
||||
|
@ -197,172 +185,6 @@ describe('http service', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('legacy server', () => {
|
||||
describe('#registerAuth()', () => {
|
||||
const sessionDurationMs = 1000;
|
||||
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
beforeEach(async () => {
|
||||
root = kbnTestServer.createRoot({ plugins: { initialize: false } });
|
||||
}, 30000);
|
||||
|
||||
afterEach(async () => {
|
||||
MockLegacyScopedClusterClient.mockClear();
|
||||
await root.shutdown();
|
||||
});
|
||||
|
||||
it('runs auth for legacy routes and proxy request to legacy server route handlers', async () => {
|
||||
const { http } = await root.setup();
|
||||
const sessionStorageFactory = await http.createCookieSessionStorageFactory<StorageData>(
|
||||
cookieOptions
|
||||
);
|
||||
http.registerAuth((req, res, toolkit) => {
|
||||
if (req.headers.authorization) {
|
||||
const user = { id: '42' };
|
||||
const sessionStorage = sessionStorageFactory.asScoped(req);
|
||||
sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs });
|
||||
return toolkit.authenticated({ state: user });
|
||||
} else {
|
||||
return res.unauthorized();
|
||||
}
|
||||
});
|
||||
await root.start();
|
||||
|
||||
const legacyUrl = '/legacy';
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: legacyUrl,
|
||||
handler: () => 'ok from legacy server',
|
||||
});
|
||||
|
||||
const response = await kbnTestServer.request
|
||||
.get(root, legacyUrl)
|
||||
.expect(200, 'ok from legacy server');
|
||||
|
||||
expect(response.header['set-cookie']).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('passes authHeaders as request headers to the legacy platform', async () => {
|
||||
const token = 'Basic: name:password';
|
||||
const { http } = await root.setup();
|
||||
const sessionStorageFactory = await http.createCookieSessionStorageFactory<StorageData>(
|
||||
cookieOptions
|
||||
);
|
||||
http.registerAuth((req, res, toolkit) => {
|
||||
if (req.headers.authorization) {
|
||||
const user = { id: '42' };
|
||||
const sessionStorage = sessionStorageFactory.asScoped(req);
|
||||
sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs });
|
||||
return toolkit.authenticated({
|
||||
state: user,
|
||||
requestHeaders: {
|
||||
authorization: token,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return res.unauthorized();
|
||||
}
|
||||
});
|
||||
await root.start();
|
||||
|
||||
const legacyUrl = '/legacy';
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: legacyUrl,
|
||||
handler: (req: Request) => ({
|
||||
authorization: req.headers.authorization,
|
||||
custom: req.headers.custom,
|
||||
}),
|
||||
});
|
||||
|
||||
await kbnTestServer.request
|
||||
.get(root, legacyUrl)
|
||||
.set({ custom: 'custom-header' })
|
||||
.expect(200, { authorization: token, custom: 'custom-header' });
|
||||
});
|
||||
|
||||
it('attach security header to a successful response handled by Legacy platform', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { http } = await root.setup();
|
||||
const { registerAuth } = http;
|
||||
|
||||
registerAuth((req, res, toolkit) => {
|
||||
return toolkit.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
await root.start();
|
||||
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: '/legacy',
|
||||
handler: () => 'ok',
|
||||
});
|
||||
|
||||
const response = await kbnTestServer.request.get(root, '/legacy').expect(200);
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
|
||||
it('attach security header to an error response handled by Legacy platform', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { http } = await root.setup();
|
||||
const { registerAuth } = http;
|
||||
|
||||
registerAuth((req, res, toolkit) => {
|
||||
return toolkit.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
await root.start();
|
||||
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: '/legacy',
|
||||
handler: () => {
|
||||
throw Boom.badRequest();
|
||||
},
|
||||
});
|
||||
|
||||
const response = await kbnTestServer.request.get(root, '/legacy').expect(400);
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#basePath()', () => {
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
beforeEach(async () => {
|
||||
root = kbnTestServer.createRoot({ plugins: { initialize: false } });
|
||||
}, 30000);
|
||||
|
||||
afterEach(async () => await root.shutdown());
|
||||
it('basePath information for an incoming request is available in legacy server', async () => {
|
||||
const reqBasePath = '/requests-specific-base-path';
|
||||
const { http } = await root.setup();
|
||||
http.registerOnPreRouting((req, res, toolkit) => {
|
||||
http.basePath.set(req, reqBasePath);
|
||||
return toolkit.next();
|
||||
});
|
||||
|
||||
await root.start();
|
||||
|
||||
const legacyUrl = '/legacy';
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: legacyUrl,
|
||||
handler: kbnServer.newPlatform.setup.core.http.basePath.get,
|
||||
});
|
||||
|
||||
await kbnTestServer.request.get(root, legacyUrl).expect(200, reqBasePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('legacy elasticsearch client', () => {
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
beforeEach(async () => {
|
||||
|
|
|
@ -14,7 +14,7 @@ const mockGetTranslationPaths = getTranslationPaths as jest.Mock;
|
|||
jest.mock('./get_translation_paths', () => ({
|
||||
getTranslationPaths: jest.fn().mockResolvedValue([]),
|
||||
}));
|
||||
jest.mock('../utils', () => ({
|
||||
jest.mock('@kbn/utils', () => ({
|
||||
fromRoot: jest.fn().mockImplementation((path: string) => path),
|
||||
}));
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import { basename } from 'path';
|
||||
import { fromRoot } from '../utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
import { getTranslationPaths } from './get_translation_paths';
|
||||
|
||||
export const getKibanaTranslationFiles = async (
|
||||
|
|
|
@ -406,8 +406,6 @@ export type {
|
|||
SavedObjectsMigrationVersion,
|
||||
} from './types';
|
||||
|
||||
export type { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig } from './legacy';
|
||||
|
||||
export { ServiceStatusLevels } from './status';
|
||||
export type { CoreStatus, ServiceStatus, ServiceStatusLevel, StatusServiceSetup } from './status';
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`once LegacyService is set up with connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"logging": Object {
|
||||
"verbose": true,
|
||||
},
|
||||
"path": Object {},
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`once LegacyService is set up without connection info reconfigures logging configuration if new config is received.: applyLoggingConfiguration params 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"logging": Object {
|
||||
"verbose": true,
|
||||
},
|
||||
"path": Object {},
|
||||
},
|
||||
],
|
||||
]
|
||||
`;
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ensureValidConfiguration } from './ensure_valid_configuration';
|
||||
import { getUnusedConfigKeys } from './get_unused_config_keys';
|
||||
import { configServiceMock } from '../../config/mocks';
|
||||
|
||||
jest.mock('./get_unused_config_keys');
|
||||
|
||||
describe('ensureValidConfiguration', () => {
|
||||
let configService: ReturnType<typeof configServiceMock.create>;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
configService = configServiceMock.create();
|
||||
configService.getUsedPaths.mockReturnValue(Promise.resolve(['core', 'elastic']));
|
||||
|
||||
(getUnusedConfigKeys as any).mockImplementation(() => []);
|
||||
});
|
||||
|
||||
it('calls getUnusedConfigKeys with correct parameters', async () => {
|
||||
await ensureValidConfiguration(
|
||||
configService as any,
|
||||
{
|
||||
settings: 'settings',
|
||||
legacyConfig: 'pluginExtendedConfig',
|
||||
} as any
|
||||
);
|
||||
expect(getUnusedConfigKeys).toHaveBeenCalledTimes(1);
|
||||
expect(getUnusedConfigKeys).toHaveBeenCalledWith({
|
||||
coreHandledConfigPaths: ['core', 'elastic'],
|
||||
settings: 'settings',
|
||||
legacyConfig: 'pluginExtendedConfig',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns normally when there is no unused keys', async () => {
|
||||
await expect(
|
||||
ensureValidConfiguration(configService as any, {} as any)
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(getUnusedConfigKeys).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('throws when there are some unused keys', async () => {
|
||||
(getUnusedConfigKeys as any).mockImplementation(() => ['some.key', 'some.other.key']);
|
||||
|
||||
await expect(
|
||||
ensureValidConfiguration(configService as any, {} as any)
|
||||
).rejects.toMatchInlineSnapshot(
|
||||
`[Error: Unknown configuration key(s): "some.key", "some.other.key". Check for spelling errors and ensure that expected plugins are installed.]`
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,163 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { LegacyConfig, LegacyVars } from '../types';
|
||||
import { getUnusedConfigKeys } from './get_unused_config_keys';
|
||||
|
||||
describe('getUnusedConfigKeys', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const getConfig = (values: LegacyVars = {}): LegacyConfig =>
|
||||
({
|
||||
get: () => values as any,
|
||||
} as LegacyConfig);
|
||||
|
||||
describe('not using core or plugin specs', () => {
|
||||
it('should return an empty list for empty parameters', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {},
|
||||
legacyConfig: getConfig(),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty list when config and settings have the same properties', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
presentInBoth: true,
|
||||
alsoInBoth: 'someValue',
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
presentInBoth: true,
|
||||
alsoInBoth: 'someValue',
|
||||
}),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns empty list when config has entries not present in settings', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
presentInBoth: true,
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
presentInBoth: true,
|
||||
onlyInConfig: 'someValue',
|
||||
}),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns the list of properties from settings not present in config', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
presentInBoth: true,
|
||||
onlyInSetting: 'value',
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
presentInBoth: true,
|
||||
}),
|
||||
})
|
||||
).toEqual(['onlyInSetting']);
|
||||
});
|
||||
|
||||
it('correctly handle nested properties', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
elasticsearch: {
|
||||
username: 'foo',
|
||||
password: 'bar',
|
||||
},
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
elasticsearch: {
|
||||
username: 'foo',
|
||||
onlyInConfig: 'default',
|
||||
},
|
||||
}),
|
||||
})
|
||||
).toEqual(['elasticsearch.password']);
|
||||
});
|
||||
|
||||
it('correctly handle "env" specific case', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
env: 'development',
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
env: {
|
||||
name: 'development',
|
||||
},
|
||||
}),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('correctly handle array properties', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: [],
|
||||
settings: {
|
||||
prop: ['a', 'b', 'c'],
|
||||
},
|
||||
legacyConfig: getConfig({
|
||||
prop: ['a'],
|
||||
}),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores properties managed by the new platform', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: ['core', 'foo.bar'],
|
||||
settings: {
|
||||
core: {
|
||||
prop: 'value',
|
||||
},
|
||||
foo: {
|
||||
bar: true,
|
||||
dolly: true,
|
||||
},
|
||||
},
|
||||
legacyConfig: getConfig({}),
|
||||
})
|
||||
).toEqual(['foo.dolly']);
|
||||
});
|
||||
|
||||
it('handles array values', async () => {
|
||||
expect(
|
||||
await getUnusedConfigKeys({
|
||||
coreHandledConfigPaths: ['core', 'array'],
|
||||
settings: {
|
||||
core: {
|
||||
prop: 'value',
|
||||
array: [1, 2, 3],
|
||||
},
|
||||
array: ['some', 'values'],
|
||||
},
|
||||
legacyConfig: getConfig({}),
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { difference } from 'lodash';
|
||||
import { getFlattenedObject } from '@kbn/std';
|
||||
import { hasConfigPathIntersection } from '../../config';
|
||||
import { LegacyConfig, LegacyVars } from '../types';
|
||||
|
||||
const getFlattenedKeys = (object: object) => Object.keys(getFlattenedObject(object));
|
||||
|
||||
export async function getUnusedConfigKeys({
|
||||
coreHandledConfigPaths,
|
||||
settings,
|
||||
legacyConfig,
|
||||
}: {
|
||||
coreHandledConfigPaths: string[];
|
||||
settings: LegacyVars;
|
||||
legacyConfig: LegacyConfig;
|
||||
}) {
|
||||
const inputKeys = getFlattenedKeys(settings);
|
||||
const appliedKeys = getFlattenedKeys(legacyConfig.get());
|
||||
|
||||
if (inputKeys.includes('env')) {
|
||||
// env is a special case key, see https://github.com/elastic/kibana/blob/848bf17b/src/legacy/server/config/config.js#L74
|
||||
// where it is deleted from the settings before being injected into the schema via context and
|
||||
// then renamed to `env.name` https://github.com/elastic/kibana/blob/848bf17/src/legacy/server/config/schema.js#L17
|
||||
inputKeys[inputKeys.indexOf('env')] = 'env.name';
|
||||
}
|
||||
|
||||
// Filter out keys that are marked as used in the core (e.g. by new core plugins).
|
||||
return difference(inputKeys, appliedKeys).filter(
|
||||
(unusedConfigKey) =>
|
||||
!coreHandledConfigPaths.some((usedInCoreConfigKey) =>
|
||||
hasConfigPathIntersection(unusedConfigKey, usedInCoreConfigKey)
|
||||
)
|
||||
);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { ensureValidConfiguration } from './ensure_valid_configuration';
|
|
@ -6,16 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
/** @internal */
|
||||
export { ensureValidConfiguration } from './config';
|
||||
/** @internal */
|
||||
export type { ILegacyService } from './legacy_service';
|
||||
export { LegacyService } from './legacy_service';
|
||||
/** @internal */
|
||||
export type {
|
||||
LegacyVars,
|
||||
LegacyConfig,
|
||||
LegacyServiceSetupDeps,
|
||||
LegacyServiceStartDeps,
|
||||
LegacyServiceSetupConfig,
|
||||
} from './types';
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import * as kbnTestServer from '../../../test_helpers/kbn_server';
|
||||
|
||||
describe('legacy service', () => {
|
||||
describe('http server', () => {
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
beforeEach(() => {
|
||||
root = kbnTestServer.createRoot({
|
||||
migrations: { skip: true },
|
||||
plugins: { initialize: false },
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
afterEach(async () => await root.shutdown());
|
||||
|
||||
it("handles http request in Legacy platform if New platform doesn't handle it", async () => {
|
||||
const { http } = await root.setup();
|
||||
const rootUrl = '/route';
|
||||
const router = http.createRouter(rootUrl);
|
||||
router.get({ path: '/new-platform', validate: false }, (context, req, res) =>
|
||||
res.ok({ body: 'from-new-platform' })
|
||||
);
|
||||
|
||||
await root.start();
|
||||
|
||||
const legacyPlatformUrl = `${rootUrl}/legacy-platform`;
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: legacyPlatformUrl,
|
||||
handler: () => 'ok from legacy server',
|
||||
});
|
||||
|
||||
await kbnTestServer.request.get(root, '/route/new-platform').expect(200, 'from-new-platform');
|
||||
|
||||
await kbnTestServer.request.get(root, legacyPlatformUrl).expect(200, 'ok from legacy server');
|
||||
});
|
||||
it('throws error if Legacy and New platforms register handler for the same route', async () => {
|
||||
const { http } = await root.setup();
|
||||
const rootUrl = '/route';
|
||||
const router = http.createRouter(rootUrl);
|
||||
router.get({ path: '', validate: false }, (context, req, res) =>
|
||||
res.ok({ body: 'from-new-platform' })
|
||||
);
|
||||
|
||||
await root.start();
|
||||
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
expect(() =>
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: rootUrl,
|
||||
handler: () => 'ok from legacy server',
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"New route /route conflicts with existing /route"`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -8,26 +8,14 @@
|
|||
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { LegacyService } from './legacy_service';
|
||||
import { LegacyConfig, LegacyServiceSetupDeps } from './types';
|
||||
|
||||
type LegacyServiceMock = jest.Mocked<PublicMethodsOf<LegacyService> & { legacyId: symbol }>;
|
||||
type LegacyServiceMock = jest.Mocked<PublicMethodsOf<LegacyService>>;
|
||||
|
||||
const createLegacyServiceMock = (): LegacyServiceMock => ({
|
||||
legacyId: Symbol(),
|
||||
setupLegacyConfig: jest.fn(),
|
||||
setup: jest.fn(),
|
||||
start: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
});
|
||||
|
||||
const createLegacyConfigMock = (): jest.Mocked<LegacyConfig> => ({
|
||||
get: jest.fn(),
|
||||
has: jest.fn(),
|
||||
set: jest.fn(),
|
||||
});
|
||||
|
||||
export const legacyServiceMock = {
|
||||
create: createLegacyServiceMock,
|
||||
createSetupContract: (deps: LegacyServiceSetupDeps) => createLegacyServiceMock().setup(deps),
|
||||
createLegacyConfig: createLegacyConfigMock,
|
||||
};
|
||||
|
|
18
src/core/server/legacy/legacy_service.test.mocks.ts
Normal file
18
src/core/server/legacy/legacy_service.test.mocks.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const reconfigureLoggingMock = jest.fn();
|
||||
export const setupLoggingMock = jest.fn();
|
||||
export const setupLoggingRotateMock = jest.fn();
|
||||
|
||||
jest.doMock('@kbn/legacy-logging', () => ({
|
||||
...(jest.requireActual('@kbn/legacy-logging') as any),
|
||||
reconfigureLogging: reconfigureLoggingMock,
|
||||
setupLogging: setupLoggingMock,
|
||||
setupLoggingRotate: setupLoggingRotateMock,
|
||||
}));
|
|
@ -6,35 +6,22 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
jest.mock('../../../legacy/server/kbn_server');
|
||||
import {
|
||||
setupLoggingMock,
|
||||
setupLoggingRotateMock,
|
||||
reconfigureLoggingMock,
|
||||
} from './legacy_service.test.mocks';
|
||||
|
||||
import { BehaviorSubject, throwError } from 'rxjs';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import moment from 'moment';
|
||||
import { REPO_ROOT } from '@kbn/dev-utils';
|
||||
|
||||
import KbnServer from '../../../legacy/server/kbn_server';
|
||||
import { Config, Env, ObjectToConfigAdapter } from '../config';
|
||||
import { DiscoveredPlugin } from '../plugins';
|
||||
|
||||
import { getEnvOptions, configServiceMock } from '../config/mocks';
|
||||
import { loggingSystemMock } from '../logging/logging_system.mock';
|
||||
import { contextServiceMock } from '../context/context_service.mock';
|
||||
import { httpServiceMock } from '../http/http_service.mock';
|
||||
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
|
||||
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
|
||||
import { capabilitiesServiceMock } from '../capabilities/capabilities_service.mock';
|
||||
import { httpResourcesMock } from '../http_resources/http_resources_service.mock';
|
||||
import { setupMock as renderingServiceMock } from '../rendering/__mocks__/rendering_service';
|
||||
import { environmentServiceMock } from '../environment/environment_service.mock';
|
||||
import { LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types';
|
||||
import { LegacyService } from './legacy_service';
|
||||
import { coreMock } from '../mocks';
|
||||
import { statusServiceMock } from '../status/status_service.mock';
|
||||
import { loggingServiceMock } from '../logging/logging_service.mock';
|
||||
import { metricsServiceMock } from '../metrics/metrics_service.mock';
|
||||
import { i18nServiceMock } from '../i18n/i18n_service.mock';
|
||||
import { deprecationsServiceMock } from '../deprecations/deprecations_service.mock';
|
||||
|
||||
const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;
|
||||
import { LegacyService, LegacyServiceSetupDeps } from './legacy_service';
|
||||
|
||||
let coreId: symbol;
|
||||
let env: Env;
|
||||
|
@ -42,71 +29,16 @@ let config$: BehaviorSubject<Config>;
|
|||
|
||||
let setupDeps: LegacyServiceSetupDeps;
|
||||
|
||||
let startDeps: LegacyServiceStartDeps;
|
||||
|
||||
const logger = loggingSystemMock.create();
|
||||
let configService: ReturnType<typeof configServiceMock.create>;
|
||||
let environmentSetup: ReturnType<typeof environmentServiceMock.createSetupContract>;
|
||||
|
||||
beforeEach(() => {
|
||||
coreId = Symbol();
|
||||
env = Env.createDefault(REPO_ROOT, getEnvOptions());
|
||||
configService = configServiceMock.create();
|
||||
environmentSetup = environmentServiceMock.createSetupContract();
|
||||
|
||||
MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve());
|
||||
MockKbnServer.prototype.listen = jest.fn();
|
||||
|
||||
setupDeps = {
|
||||
core: {
|
||||
capabilities: capabilitiesServiceMock.createSetupContract(),
|
||||
context: contextServiceMock.createSetupContract(),
|
||||
elasticsearch: { legacy: {} } as any,
|
||||
i18n: i18nServiceMock.createSetupContract(),
|
||||
uiSettings: uiSettingsServiceMock.createSetupContract(),
|
||||
http: {
|
||||
...httpServiceMock.createInternalSetupContract(),
|
||||
auth: {
|
||||
getAuthHeaders: () => undefined,
|
||||
} as any,
|
||||
},
|
||||
httpResources: httpResourcesMock.createSetupContract(),
|
||||
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
|
||||
plugins: {
|
||||
initialized: true,
|
||||
contracts: new Map([['plugin-id', 'plugin-value']]),
|
||||
},
|
||||
rendering: renderingServiceMock,
|
||||
environment: environmentSetup,
|
||||
status: statusServiceMock.createInternalSetupContract(),
|
||||
logging: loggingServiceMock.createInternalSetupContract(),
|
||||
metrics: metricsServiceMock.createInternalSetupContract(),
|
||||
deprecations: deprecationsServiceMock.createInternalSetupContract(),
|
||||
},
|
||||
plugins: { 'plugin-id': 'plugin-value' },
|
||||
uiPlugins: {
|
||||
public: new Map([['plugin-id', {} as DiscoveredPlugin]]),
|
||||
internal: new Map([
|
||||
[
|
||||
'plugin-id',
|
||||
{
|
||||
requiredBundles: [],
|
||||
version: '8.0.0',
|
||||
publicTargetDir: 'path/to/target/public',
|
||||
publicAssetsDir: '/plugins/name/assets/',
|
||||
},
|
||||
],
|
||||
]),
|
||||
browserConfigs: new Map(),
|
||||
},
|
||||
};
|
||||
|
||||
startDeps = {
|
||||
core: {
|
||||
...coreMock.createInternalStart(),
|
||||
plugins: { contracts: new Map() },
|
||||
},
|
||||
plugins: {},
|
||||
http: httpServiceMock.createInternalSetupContract(),
|
||||
};
|
||||
|
||||
config$ = new BehaviorSubject<Config>(
|
||||
|
@ -117,78 +49,78 @@ beforeEach(() => {
|
|||
);
|
||||
|
||||
configService.getConfig$.mockReturnValue(config$);
|
||||
configService.getUsedPaths.mockResolvedValue(['foo.bar']);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
setupLoggingMock.mockReset();
|
||||
setupLoggingRotateMock.mockReset();
|
||||
reconfigureLoggingMock.mockReset();
|
||||
});
|
||||
|
||||
describe('once LegacyService is set up with connection info', () => {
|
||||
test('creates legacy kbnServer and calls `listen`.', async () => {
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService,
|
||||
describe('#setup', () => {
|
||||
it('initializes legacy logging', async () => {
|
||||
const opsConfig = {
|
||||
interval: moment.duration(5, 'second'),
|
||||
};
|
||||
const opsConfig$ = new BehaviorSubject(opsConfig);
|
||||
|
||||
const loggingConfig = {
|
||||
foo: 'bar',
|
||||
};
|
||||
const loggingConfig$ = new BehaviorSubject(loggingConfig);
|
||||
|
||||
configService.atPath.mockImplementation((path) => {
|
||||
if (path === 'ops') {
|
||||
return opsConfig$;
|
||||
}
|
||||
if (path === 'logging') {
|
||||
return loggingConfig$;
|
||||
}
|
||||
return new BehaviorSubject({});
|
||||
});
|
||||
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
|
||||
expect(MockKbnServer).toHaveBeenCalledTimes(1);
|
||||
expect(MockKbnServer).toHaveBeenCalledWith(
|
||||
{ path: { autoListen: true }, server: { autoListen: true } }, // Because of the mock, path also gets the value
|
||||
expect.objectContaining({ get: expect.any(Function) }),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(MockKbnServer.mock.calls[0][1].get()).toEqual(
|
||||
expect.objectContaining({
|
||||
path: expect.objectContaining({ autoListen: true }),
|
||||
server: expect.objectContaining({ autoListen: true }),
|
||||
})
|
||||
);
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
||||
expect(mockKbnServer.listen).toHaveBeenCalledTimes(1);
|
||||
expect(mockKbnServer.close).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('creates legacy kbnServer but does not call `listen` if `autoListen: false`.', async () => {
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: false }));
|
||||
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
|
||||
expect(MockKbnServer).toHaveBeenCalledTimes(1);
|
||||
expect(MockKbnServer).toHaveBeenCalledWith(
|
||||
{ path: { autoListen: false }, server: { autoListen: true } },
|
||||
expect.objectContaining({ get: expect.any(Function) }),
|
||||
expect.any(Object)
|
||||
await legacyService.setup(setupDeps);
|
||||
|
||||
expect(setupLoggingMock).toHaveBeenCalledTimes(1);
|
||||
expect(setupLoggingMock).toHaveBeenCalledWith(
|
||||
setupDeps.http.server,
|
||||
loggingConfig,
|
||||
opsConfig.interval.asMilliseconds()
|
||||
);
|
||||
|
||||
const legacyConfig = MockKbnServer.mock.calls[0][1].get();
|
||||
expect(legacyConfig.path.autoListen).toBe(false);
|
||||
expect(legacyConfig.server.autoListen).toBe(true);
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
||||
expect(mockKbnServer.ready).toHaveBeenCalledTimes(1);
|
||||
expect(mockKbnServer.listen).not.toHaveBeenCalled();
|
||||
expect(mockKbnServer.close).not.toHaveBeenCalled();
|
||||
expect(setupLoggingRotateMock).toHaveBeenCalledTimes(1);
|
||||
expect(setupLoggingRotateMock).toHaveBeenCalledWith(setupDeps.http.server, loggingConfig);
|
||||
});
|
||||
|
||||
test('creates legacy kbnServer and closes it if `listen` fails.', async () => {
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
||||
MockKbnServer.prototype.listen.mockRejectedValue(new Error('something failed'));
|
||||
it('reloads the logging config when the config changes', async () => {
|
||||
const opsConfig = {
|
||||
interval: moment.duration(5, 'second'),
|
||||
};
|
||||
const opsConfig$ = new BehaviorSubject(opsConfig);
|
||||
|
||||
const loggingConfig = {
|
||||
foo: 'bar',
|
||||
};
|
||||
const loggingConfig$ = new BehaviorSubject(loggingConfig);
|
||||
|
||||
configService.atPath.mockImplementation((path) => {
|
||||
if (path === 'ops') {
|
||||
return opsConfig$;
|
||||
}
|
||||
if (path === 'logging') {
|
||||
return loggingConfig$;
|
||||
}
|
||||
return new BehaviorSubject({});
|
||||
});
|
||||
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
|
@ -196,19 +128,48 @@ describe('once LegacyService is set up with connection info', () => {
|
|||
configService: configService as any,
|
||||
});
|
||||
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"something failed"`
|
||||
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(1);
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledWith(
|
||||
setupDeps.http.server,
|
||||
loggingConfig,
|
||||
opsConfig.interval.asMilliseconds()
|
||||
);
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
||||
expect(mockKbnServer.listen).toHaveBeenCalled();
|
||||
expect(mockKbnServer.close).toHaveBeenCalled();
|
||||
loggingConfig$.next({
|
||||
foo: 'changed',
|
||||
});
|
||||
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(2);
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledWith(
|
||||
setupDeps.http.server,
|
||||
{ foo: 'changed' },
|
||||
opsConfig.interval.asMilliseconds()
|
||||
);
|
||||
});
|
||||
|
||||
test('throws if fails to retrieve initial config.', async () => {
|
||||
configService.getConfig$.mockReturnValue(throwError(new Error('something failed')));
|
||||
it('stops reloading logging config once the service is stopped', async () => {
|
||||
const opsConfig = {
|
||||
interval: moment.duration(5, 'second'),
|
||||
};
|
||||
const opsConfig$ = new BehaviorSubject(opsConfig);
|
||||
|
||||
const loggingConfig = {
|
||||
foo: 'bar',
|
||||
};
|
||||
const loggingConfig$ = new BehaviorSubject(loggingConfig);
|
||||
|
||||
configService.atPath.mockImplementation((path) => {
|
||||
if (path === 'ops') {
|
||||
return opsConfig$;
|
||||
}
|
||||
if (path === 'logging') {
|
||||
return loggingConfig$;
|
||||
}
|
||||
return new BehaviorSubject({});
|
||||
});
|
||||
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
|
@ -216,150 +177,21 @@ describe('once LegacyService is set up with connection info', () => {
|
|||
configService: configService as any,
|
||||
});
|
||||
|
||||
await expect(legacyService.setupLegacyConfig()).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"something failed"`
|
||||
);
|
||||
await expect(legacyService.setup(setupDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Legacy config not initialized yet. Ensure LegacyService.setupLegacyConfig() is called before LegacyService.setup()"`
|
||||
);
|
||||
await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Legacy service is not setup yet."`
|
||||
);
|
||||
|
||||
expect(MockKbnServer).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('reconfigures logging configuration if new config is received.', async () => {
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
|
||||
config$.next(new ObjectToConfigAdapter({ logging: { verbose: true } }));
|
||||
|
||||
expect(mockKbnServer.applyLoggingConfiguration.mock.calls).toMatchSnapshot(
|
||||
`applyLoggingConfiguration params`
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(1);
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledWith(
|
||||
setupDeps.http.server,
|
||||
loggingConfig,
|
||||
opsConfig.interval.asMilliseconds()
|
||||
);
|
||||
});
|
||||
|
||||
test('logs error if re-configuring fails.', async () => {
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
await legacyService.stop();
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(loggingSystemMock.collect(logger).error).toEqual([]);
|
||||
|
||||
const configError = new Error('something went wrong');
|
||||
mockKbnServer.applyLoggingConfiguration.mockImplementation(() => {
|
||||
throw configError;
|
||||
loggingConfig$.next({
|
||||
foo: 'changed',
|
||||
});
|
||||
|
||||
config$.next(new ObjectToConfigAdapter({ logging: { verbose: true } }));
|
||||
|
||||
expect(loggingSystemMock.collect(logger).error).toEqual([[configError]]);
|
||||
});
|
||||
|
||||
test('logs error if config service fails.', async () => {
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(loggingSystemMock.collect(logger).error).toEqual([]);
|
||||
|
||||
const configError = new Error('something went wrong');
|
||||
config$.error(configError);
|
||||
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
expect(loggingSystemMock.collect(logger).error).toEqual([[configError]]);
|
||||
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('once LegacyService is set up without connection info', () => {
|
||||
let legacyService: LegacyService;
|
||||
beforeEach(async () => {
|
||||
legacyService = new LegacyService({ coreId, env, logger, configService: configService as any });
|
||||
await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
await legacyService.start(startDeps);
|
||||
});
|
||||
|
||||
test('creates legacy kbnServer with `autoListen: false`.', () => {
|
||||
expect(MockKbnServer).toHaveBeenCalledTimes(1);
|
||||
expect(MockKbnServer).toHaveBeenCalledWith(
|
||||
{ path: {}, server: { autoListen: true } },
|
||||
expect.objectContaining({ get: expect.any(Function) }),
|
||||
expect.any(Object)
|
||||
);
|
||||
expect(MockKbnServer.mock.calls[0][1].get()).toEqual(
|
||||
expect.objectContaining({
|
||||
server: expect.objectContaining({ autoListen: true }),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('reconfigures logging configuration if new config is received.', async () => {
|
||||
const [mockKbnServer] = MockKbnServer.mock.instances as Array<jest.Mocked<KbnServer>>;
|
||||
expect(mockKbnServer.applyLoggingConfiguration).not.toHaveBeenCalled();
|
||||
|
||||
config$.next(new ObjectToConfigAdapter({ logging: { verbose: true } }));
|
||||
|
||||
expect(mockKbnServer.applyLoggingConfiguration.mock.calls).toMatchSnapshot(
|
||||
`applyLoggingConfiguration params`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
test('Cannot start without setup phase', async () => {
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
await expect(legacyService.start(startDeps)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"Legacy service is not setup yet."`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('Sets the server.uuid property on the legacy configuration', async () => {
|
||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
||||
const legacyService = new LegacyService({
|
||||
coreId,
|
||||
env,
|
||||
logger,
|
||||
configService: configService as any,
|
||||
});
|
||||
|
||||
environmentSetup.instanceUuid = 'UUID_FROM_SERVICE';
|
||||
|
||||
const { legacyConfig } = await legacyService.setupLegacyConfig();
|
||||
await legacyService.setup(setupDeps);
|
||||
|
||||
expect(legacyConfig.get('server.uuid')).toBe('UUID_FROM_SERVICE');
|
||||
});
|
||||
|
|
|
@ -6,141 +6,61 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { combineLatest, ConnectableObservable, Observable, Subscription } from 'rxjs';
|
||||
import { first, map, publishReplay, tap } from 'rxjs/operators';
|
||||
import { combineLatest, Observable, Subscription } from 'rxjs';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { Server } from '@hapi/hapi';
|
||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import { PathConfigType } from '@kbn/utils';
|
||||
|
||||
import type { RequestHandlerContext } from 'src/core/server';
|
||||
// @ts-expect-error legacy config class
|
||||
import { Config as LegacyConfigClass } from '../../../legacy/server/config';
|
||||
import { CoreService } from '../../types';
|
||||
import { Config } from '../config';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { CspConfigType, config as cspConfig } from '../csp';
|
||||
import {
|
||||
HttpConfig,
|
||||
HttpConfigType,
|
||||
config as httpConfig,
|
||||
IRouter,
|
||||
RequestHandlerContextProvider,
|
||||
} from '../http';
|
||||
reconfigureLogging,
|
||||
setupLogging,
|
||||
setupLoggingRotate,
|
||||
LegacyLoggingConfig,
|
||||
} from '@kbn/legacy-logging';
|
||||
|
||||
import { CoreContext } from '../core_context';
|
||||
import { config as loggingConfig } from '../logging';
|
||||
import { opsConfig, OpsConfigType } from '../metrics';
|
||||
import { Logger } from '../logging';
|
||||
import { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig, LegacyVars } from './types';
|
||||
import { ExternalUrlConfigType, config as externalUrlConfig } from '../external_url';
|
||||
import { CoreSetup, CoreStart } from '..';
|
||||
import { InternalHttpServiceSetup } from '../http';
|
||||
|
||||
interface LegacyKbnServer {
|
||||
applyLoggingConfiguration: (settings: Readonly<LegacyVars>) => void;
|
||||
listen: () => Promise<void>;
|
||||
ready: () => Promise<void>;
|
||||
close: () => Promise<void>;
|
||||
}
|
||||
|
||||
function getLegacyRawConfig(config: Config, pathConfig: PathConfigType) {
|
||||
const rawConfig = config.toRaw();
|
||||
|
||||
// Elasticsearch config is solely handled by the core and legacy platform
|
||||
// shouldn't have direct access to it.
|
||||
if (rawConfig.elasticsearch !== undefined) {
|
||||
delete rawConfig.elasticsearch;
|
||||
}
|
||||
|
||||
return {
|
||||
...rawConfig,
|
||||
// We rely heavily in the default value of 'path.data' in the legacy world and,
|
||||
// since it has been moved to NP, it won't show up in RawConfig.
|
||||
path: pathConfig,
|
||||
};
|
||||
export interface LegacyServiceSetupDeps {
|
||||
http: InternalHttpServiceSetup;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export type ILegacyService = PublicMethodsOf<LegacyService>;
|
||||
|
||||
/** @internal */
|
||||
export class LegacyService implements CoreService {
|
||||
/** Symbol to represent the legacy platform as a fake "plugin". Used by the ContextService */
|
||||
public readonly legacyId = Symbol();
|
||||
export class LegacyService {
|
||||
private readonly log: Logger;
|
||||
private readonly httpConfig$: Observable<HttpConfig>;
|
||||
private kbnServer?: LegacyKbnServer;
|
||||
private readonly opsConfig$: Observable<OpsConfigType>;
|
||||
private readonly legacyLoggingConfig$: Observable<LegacyLoggingConfig>;
|
||||
private configSubscription?: Subscription;
|
||||
private setupDeps?: LegacyServiceSetupDeps;
|
||||
private update$?: ConnectableObservable<[Config, PathConfigType]>;
|
||||
private legacyRawConfig?: LegacyConfig;
|
||||
private settings?: LegacyVars;
|
||||
|
||||
constructor(private readonly coreContext: CoreContext) {
|
||||
constructor(coreContext: CoreContext) {
|
||||
const { logger, configService } = coreContext;
|
||||
|
||||
this.log = logger.get('legacy-service');
|
||||
this.httpConfig$ = combineLatest(
|
||||
configService.atPath<HttpConfigType>(httpConfig.path),
|
||||
configService.atPath<CspConfigType>(cspConfig.path),
|
||||
configService.atPath<ExternalUrlConfigType>(externalUrlConfig.path)
|
||||
).pipe(map(([http, csp, externalUrl]) => new HttpConfig(http, csp, externalUrl)));
|
||||
}
|
||||
|
||||
public async setupLegacyConfig() {
|
||||
this.update$ = combineLatest([
|
||||
this.coreContext.configService.getConfig$(),
|
||||
this.coreContext.configService.atPath<PathConfigType>('path'),
|
||||
]).pipe(
|
||||
tap(([config, pathConfig]) => {
|
||||
if (this.kbnServer !== undefined) {
|
||||
this.kbnServer.applyLoggingConfiguration(getLegacyRawConfig(config, pathConfig));
|
||||
}
|
||||
}),
|
||||
tap({ error: (err) => this.log.error(err) }),
|
||||
publishReplay(1)
|
||||
) as ConnectableObservable<[Config, PathConfigType]>;
|
||||
|
||||
this.configSubscription = this.update$.connect();
|
||||
|
||||
this.settings = await this.update$
|
||||
.pipe(
|
||||
first(),
|
||||
map(([config, pathConfig]) => getLegacyRawConfig(config, pathConfig))
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
this.legacyRawConfig = LegacyConfigClass.withDefaultSchema(this.settings);
|
||||
|
||||
return {
|
||||
settings: this.settings,
|
||||
legacyConfig: this.legacyRawConfig!,
|
||||
};
|
||||
this.legacyLoggingConfig$ = configService.atPath<LegacyLoggingConfig>(loggingConfig.path);
|
||||
this.opsConfig$ = configService.atPath<OpsConfigType>(opsConfig.path);
|
||||
}
|
||||
|
||||
public async setup(setupDeps: LegacyServiceSetupDeps) {
|
||||
this.log.debug('setting up legacy service');
|
||||
|
||||
if (!this.legacyRawConfig) {
|
||||
throw new Error(
|
||||
'Legacy config not initialized yet. Ensure LegacyService.setupLegacyConfig() is called before LegacyService.setup()'
|
||||
);
|
||||
}
|
||||
|
||||
// propagate the instance uuid to the legacy config, as it was the legacy way to access it.
|
||||
this.legacyRawConfig!.set('server.uuid', setupDeps.core.environment.instanceUuid);
|
||||
|
||||
this.setupDeps = setupDeps;
|
||||
await this.setupLegacyLogging(setupDeps.http.server);
|
||||
}
|
||||
|
||||
public async start(startDeps: LegacyServiceStartDeps) {
|
||||
const { setupDeps } = this;
|
||||
private async setupLegacyLogging(server: Server) {
|
||||
const legacyLoggingConfig = await this.legacyLoggingConfig$.pipe(first()).toPromise();
|
||||
const currentOpsConfig = await this.opsConfig$.pipe(first()).toPromise();
|
||||
|
||||
if (!setupDeps || !this.legacyRawConfig) {
|
||||
throw new Error('Legacy service is not setup yet.');
|
||||
}
|
||||
await setupLogging(server, legacyLoggingConfig, currentOpsConfig.interval.asMilliseconds());
|
||||
await setupLoggingRotate(server, legacyLoggingConfig);
|
||||
|
||||
this.log.debug('starting legacy service');
|
||||
|
||||
this.kbnServer = await this.createKbnServer(
|
||||
this.settings!,
|
||||
this.legacyRawConfig!,
|
||||
setupDeps,
|
||||
startDeps
|
||||
this.configSubscription = combineLatest([this.legacyLoggingConfig$, this.opsConfig$]).subscribe(
|
||||
([newLoggingConfig, newOpsConfig]) => {
|
||||
reconfigureLogging(server, newLoggingConfig, newOpsConfig.interval.asMilliseconds());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -151,156 +71,5 @@ export class LegacyService implements CoreService {
|
|||
this.configSubscription.unsubscribe();
|
||||
this.configSubscription = undefined;
|
||||
}
|
||||
|
||||
if (this.kbnServer !== undefined) {
|
||||
await this.kbnServer.close();
|
||||
this.kbnServer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async createKbnServer(
|
||||
settings: LegacyVars,
|
||||
config: LegacyConfig,
|
||||
setupDeps: LegacyServiceSetupDeps,
|
||||
startDeps: LegacyServiceStartDeps
|
||||
) {
|
||||
const coreStart: CoreStart = {
|
||||
capabilities: startDeps.core.capabilities,
|
||||
elasticsearch: startDeps.core.elasticsearch,
|
||||
http: {
|
||||
auth: startDeps.core.http.auth,
|
||||
basePath: startDeps.core.http.basePath,
|
||||
getServerInfo: startDeps.core.http.getServerInfo,
|
||||
},
|
||||
savedObjects: {
|
||||
getScopedClient: startDeps.core.savedObjects.getScopedClient,
|
||||
createScopedRepository: startDeps.core.savedObjects.createScopedRepository,
|
||||
createInternalRepository: startDeps.core.savedObjects.createInternalRepository,
|
||||
createSerializer: startDeps.core.savedObjects.createSerializer,
|
||||
createExporter: startDeps.core.savedObjects.createExporter,
|
||||
createImporter: startDeps.core.savedObjects.createImporter,
|
||||
getTypeRegistry: startDeps.core.savedObjects.getTypeRegistry,
|
||||
},
|
||||
metrics: {
|
||||
collectionInterval: startDeps.core.metrics.collectionInterval,
|
||||
getOpsMetrics$: startDeps.core.metrics.getOpsMetrics$,
|
||||
},
|
||||
uiSettings: { asScopedToClient: startDeps.core.uiSettings.asScopedToClient },
|
||||
coreUsageData: {
|
||||
getCoreUsageData: () => {
|
||||
throw new Error('core.start.coreUsageData.getCoreUsageData is unsupported in legacy');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const router = setupDeps.core.http.createRouter('', this.legacyId);
|
||||
const coreSetup: CoreSetup = {
|
||||
capabilities: setupDeps.core.capabilities,
|
||||
context: setupDeps.core.context,
|
||||
elasticsearch: {
|
||||
legacy: setupDeps.core.elasticsearch.legacy,
|
||||
},
|
||||
http: {
|
||||
createCookieSessionStorageFactory: setupDeps.core.http.createCookieSessionStorageFactory,
|
||||
registerRouteHandlerContext: <
|
||||
Context extends RequestHandlerContext,
|
||||
ContextName extends keyof Context
|
||||
>(
|
||||
contextName: ContextName,
|
||||
provider: RequestHandlerContextProvider<Context, ContextName>
|
||||
) => setupDeps.core.http.registerRouteHandlerContext(this.legacyId, contextName, provider),
|
||||
createRouter: <Context extends RequestHandlerContext = RequestHandlerContext>() =>
|
||||
router as IRouter<Context>,
|
||||
resources: setupDeps.core.httpResources.createRegistrar(router),
|
||||
registerOnPreRouting: setupDeps.core.http.registerOnPreRouting,
|
||||
registerOnPreAuth: setupDeps.core.http.registerOnPreAuth,
|
||||
registerAuth: setupDeps.core.http.registerAuth,
|
||||
registerOnPostAuth: setupDeps.core.http.registerOnPostAuth,
|
||||
registerOnPreResponse: setupDeps.core.http.registerOnPreResponse,
|
||||
basePath: setupDeps.core.http.basePath,
|
||||
auth: {
|
||||
get: setupDeps.core.http.auth.get,
|
||||
isAuthenticated: setupDeps.core.http.auth.isAuthenticated,
|
||||
},
|
||||
csp: setupDeps.core.http.csp,
|
||||
getServerInfo: setupDeps.core.http.getServerInfo,
|
||||
},
|
||||
i18n: setupDeps.core.i18n,
|
||||
logging: {
|
||||
configure: (config$) => setupDeps.core.logging.configure([], config$),
|
||||
},
|
||||
metrics: {
|
||||
collectionInterval: setupDeps.core.metrics.collectionInterval,
|
||||
getOpsMetrics$: setupDeps.core.metrics.getOpsMetrics$,
|
||||
},
|
||||
savedObjects: {
|
||||
setClientFactoryProvider: setupDeps.core.savedObjects.setClientFactoryProvider,
|
||||
addClientWrapper: setupDeps.core.savedObjects.addClientWrapper,
|
||||
registerType: setupDeps.core.savedObjects.registerType,
|
||||
},
|
||||
status: {
|
||||
isStatusPageAnonymous: setupDeps.core.status.isStatusPageAnonymous,
|
||||
core$: setupDeps.core.status.core$,
|
||||
overall$: setupDeps.core.status.overall$,
|
||||
set: () => {
|
||||
throw new Error(`core.status.set is unsupported in legacy`);
|
||||
},
|
||||
// @ts-expect-error
|
||||
get dependencies$() {
|
||||
throw new Error(`core.status.dependencies$ is unsupported in legacy`);
|
||||
},
|
||||
// @ts-expect-error
|
||||
get derivedStatus$() {
|
||||
throw new Error(`core.status.derivedStatus$ is unsupported in legacy`);
|
||||
},
|
||||
},
|
||||
uiSettings: {
|
||||
register: setupDeps.core.uiSettings.register,
|
||||
},
|
||||
deprecations: {
|
||||
registerDeprecations: () => {
|
||||
throw new Error('core.setup.deprecations.registerDeprecations is unsupported in legacy');
|
||||
},
|
||||
},
|
||||
getStartServices: () => Promise.resolve([coreStart, startDeps.plugins, {}]),
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const KbnServer = require('../../../legacy/server/kbn_server');
|
||||
const kbnServer: LegacyKbnServer = new KbnServer(settings, config, {
|
||||
env: {
|
||||
mode: this.coreContext.env.mode,
|
||||
packageInfo: this.coreContext.env.packageInfo,
|
||||
},
|
||||
setupDeps: {
|
||||
core: coreSetup,
|
||||
plugins: setupDeps.plugins,
|
||||
},
|
||||
startDeps: {
|
||||
core: coreStart,
|
||||
plugins: startDeps.plugins,
|
||||
},
|
||||
__internals: {
|
||||
hapiServer: setupDeps.core.http.server,
|
||||
uiPlugins: setupDeps.uiPlugins,
|
||||
rendering: setupDeps.core.rendering,
|
||||
},
|
||||
logger: this.coreContext.logger,
|
||||
});
|
||||
|
||||
const { autoListen } = await this.httpConfig$.pipe(first()).toPromise();
|
||||
|
||||
if (autoListen) {
|
||||
try {
|
||||
await kbnServer.listen();
|
||||
} catch (err) {
|
||||
await kbnServer.close();
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
await kbnServer.ready();
|
||||
}
|
||||
|
||||
return kbnServer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
import { schema } from '@kbn/config-schema';
|
||||
import { LegacyLoggingServer } from '@kbn/legacy-logging';
|
||||
import { DisposableAppender, LogRecord } from '@kbn/logging';
|
||||
import { LegacyVars } from '../../types';
|
||||
|
||||
export interface LegacyAppenderConfig {
|
||||
type: 'legacy-appender';
|
||||
legacyLoggingConfig?: any;
|
||||
legacyLoggingConfig?: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,7 +22,7 @@ export interface LegacyAppenderConfig {
|
|||
export class LegacyAppender implements DisposableAppender {
|
||||
public static configSchema = schema.object({
|
||||
type: schema.literal('legacy-appender'),
|
||||
legacyLoggingConfig: schema.any(),
|
||||
legacyLoggingConfig: schema.recordOf(schema.string(), schema.any()),
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -34,7 +33,7 @@ export class LegacyAppender implements DisposableAppender {
|
|||
|
||||
private readonly loggingServer: LegacyLoggingServer;
|
||||
|
||||
constructor(legacyLoggingConfig: Readonly<LegacyVars>) {
|
||||
constructor(legacyLoggingConfig: any) {
|
||||
this.loggingServer = new LegacyLoggingServer(legacyLoggingConfig);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { mergeVars } from './merge_vars';
|
||||
|
||||
describe('mergeVars', () => {
|
||||
it('merges two objects together', () => {
|
||||
const first = {
|
||||
otherName: 'value',
|
||||
otherCanFoo: true,
|
||||
otherNested: {
|
||||
otherAnotherVariable: 'ok',
|
||||
},
|
||||
};
|
||||
const second = {
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
nested: {
|
||||
anotherVariable: 'ok',
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeVars(first, second)).toEqual({
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
nested: {
|
||||
anotherVariable: 'ok',
|
||||
},
|
||||
otherName: 'value',
|
||||
otherCanFoo: true,
|
||||
otherNested: {
|
||||
otherAnotherVariable: 'ok',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not mutate the source objects', () => {
|
||||
const first = {
|
||||
var1: 'first',
|
||||
};
|
||||
const second = {
|
||||
var1: 'second',
|
||||
var2: 'second',
|
||||
};
|
||||
const third = {
|
||||
var1: 'third',
|
||||
var2: 'third',
|
||||
var3: 'third',
|
||||
};
|
||||
const fourth = {
|
||||
var1: 'fourth',
|
||||
var2: 'fourth',
|
||||
var3: 'fourth',
|
||||
var4: 'fourth',
|
||||
};
|
||||
|
||||
mergeVars(first, second, third, fourth);
|
||||
|
||||
expect(first).toEqual({ var1: 'first' });
|
||||
expect(second).toEqual({ var1: 'second', var2: 'second' });
|
||||
expect(third).toEqual({ var1: 'third', var2: 'third', var3: 'third' });
|
||||
expect(fourth).toEqual({ var1: 'fourth', var2: 'fourth', var3: 'fourth', var4: 'fourth' });
|
||||
});
|
||||
|
||||
it('merges multiple objects together with precedence increasing from left-to-right', () => {
|
||||
const first = {
|
||||
var1: 'first',
|
||||
var2: 'first',
|
||||
var3: 'first',
|
||||
var4: 'first',
|
||||
};
|
||||
const second = {
|
||||
var1: 'second',
|
||||
var2: 'second',
|
||||
var3: 'second',
|
||||
};
|
||||
const third = {
|
||||
var1: 'third',
|
||||
var2: 'third',
|
||||
};
|
||||
const fourth = {
|
||||
var1: 'fourth',
|
||||
};
|
||||
|
||||
expect(mergeVars(first, second, third, fourth)).toEqual({
|
||||
var1: 'fourth',
|
||||
var2: 'third',
|
||||
var3: 'second',
|
||||
var4: 'first',
|
||||
});
|
||||
});
|
||||
|
||||
it('overwrites the original variable value if a duplicate entry is found', () => {
|
||||
const first = {
|
||||
nested: {
|
||||
otherAnotherVariable: 'ok',
|
||||
},
|
||||
};
|
||||
const second = {
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
nested: {
|
||||
anotherVariable: 'ok',
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeVars(first, second)).toEqual({
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
nested: {
|
||||
anotherVariable: 'ok',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('combines entries within "uiCapabilities"', () => {
|
||||
const first = {
|
||||
uiCapabilities: {
|
||||
firstCapability: 'ok',
|
||||
sharedCapability: 'shared',
|
||||
},
|
||||
};
|
||||
const second = {
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
uiCapabilities: {
|
||||
secondCapability: 'ok',
|
||||
},
|
||||
};
|
||||
const third = {
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
uiCapabilities: {
|
||||
thirdCapability: 'ok',
|
||||
sharedCapability: 'blocked',
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeVars(first, second, third)).toEqual({
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
uiCapabilities: {
|
||||
firstCapability: 'ok',
|
||||
secondCapability: 'ok',
|
||||
thirdCapability: 'ok',
|
||||
sharedCapability: 'blocked',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not deeply combine entries within "uiCapabilities"', () => {
|
||||
const first = {
|
||||
uiCapabilities: {
|
||||
firstCapability: 'ok',
|
||||
nestedCapability: {
|
||||
otherNestedProp: 'otherNestedValue',
|
||||
},
|
||||
},
|
||||
};
|
||||
const second = {
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
uiCapabilities: {
|
||||
secondCapability: 'ok',
|
||||
nestedCapability: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(mergeVars(first, second)).toEqual({
|
||||
name: 'value',
|
||||
canFoo: true,
|
||||
uiCapabilities: {
|
||||
firstCapability: 'ok',
|
||||
secondCapability: 'ok',
|
||||
nestedCapability: {
|
||||
nestedProp: 'nestedValue',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { LegacyVars } from './types';
|
||||
|
||||
const ELIGIBLE_FLAT_MERGE_KEYS = ['uiCapabilities'];
|
||||
|
||||
export function mergeVars(...sources: LegacyVars[]): LegacyVars {
|
||||
return Object.assign(
|
||||
{},
|
||||
...sources,
|
||||
...ELIGIBLE_FLAT_MERGE_KEYS.flatMap((key) =>
|
||||
sources.some((source) => key in source)
|
||||
? [{ [key]: Object.assign({}, ...sources.map((source) => source[key] || {})) }]
|
||||
: []
|
||||
)
|
||||
);
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { InternalCoreSetup, InternalCoreStart } from '../internal_types';
|
||||
import { PluginsServiceSetup, PluginsServiceStart, UiPlugins } from '../plugins';
|
||||
import { InternalRenderingServiceSetup } from '../rendering';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export type LegacyVars = Record<string, any>;
|
||||
|
||||
type LegacyCoreSetup = InternalCoreSetup & {
|
||||
plugins: PluginsServiceSetup;
|
||||
rendering: InternalRenderingServiceSetup;
|
||||
};
|
||||
type LegacyCoreStart = InternalCoreStart & { plugins: PluginsServiceStart };
|
||||
|
||||
/**
|
||||
* New platform representation of the legacy configuration (KibanaConfig)
|
||||
*
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface LegacyConfig {
|
||||
get<T>(key?: string): T;
|
||||
has(key: string): boolean;
|
||||
set(key: string, value: any): void;
|
||||
set(config: LegacyVars): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated
|
||||
*/
|
||||
export interface LegacyServiceSetupDeps {
|
||||
core: LegacyCoreSetup;
|
||||
plugins: Record<string, unknown>;
|
||||
uiPlugins: UiPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated
|
||||
*/
|
||||
export interface LegacyServiceStartDeps {
|
||||
core: LegacyCoreStart;
|
||||
plugins: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated
|
||||
*/
|
||||
export interface LegacyServiceSetupConfig {
|
||||
legacyConfig: LegacyConfig;
|
||||
settings: LegacyVars;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`\`schema\` creates correct schema with defaults. 1`] = `
|
||||
Object {
|
||||
"appenders": Map {},
|
||||
"loggers": Array [],
|
||||
"root": Object {
|
||||
"appenders": Array [
|
||||
"default",
|
||||
],
|
||||
"level": "info",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`\`schema\` throws if \`root\` logger does not have "default" appender configured. 1`] = `"[root]: \\"default\\" appender required for migration period till the next major release"`;
|
||||
|
||||
exports[`\`schema\` throws if \`root\` logger does not have appenders configured. 1`] = `"[root.appenders]: array size is [0], but cannot be smaller than [1]"`;
|
||||
|
||||
exports[`fails if loggers use unknown appenders. 1`] = `"Logger \\"some.nested.context\\" contains unsupported appender key \\"unknown\\"."`;
|
|
@ -9,7 +9,35 @@
|
|||
import { LoggingConfig, config } from './logging_config';
|
||||
|
||||
test('`schema` creates correct schema with defaults.', () => {
|
||||
expect(config.schema.validate({})).toMatchSnapshot();
|
||||
expect(config.schema.validate({})).toMatchInlineSnapshot(
|
||||
{ json: expect.any(Boolean) }, // default value depends on TTY
|
||||
`
|
||||
Object {
|
||||
"appenders": Map {},
|
||||
"dest": "stdout",
|
||||
"events": Object {},
|
||||
"filter": Object {},
|
||||
"json": Any<Boolean>,
|
||||
"loggers": Array [],
|
||||
"quiet": false,
|
||||
"root": Object {
|
||||
"appenders": Array [
|
||||
"default",
|
||||
],
|
||||
"level": "info",
|
||||
},
|
||||
"rotate": Object {
|
||||
"enabled": false,
|
||||
"everyBytes": 10485760,
|
||||
"keepFiles": 7,
|
||||
"pollingInterval": 10000,
|
||||
"usePolling": false,
|
||||
},
|
||||
"silent": false,
|
||||
"verbose": false,
|
||||
}
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
test('`schema` throws if `root` logger does not have appenders configured.', () => {
|
||||
|
@ -19,7 +47,9 @@ test('`schema` throws if `root` logger does not have appenders configured.', ()
|
|||
appenders: [],
|
||||
},
|
||||
})
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[root.appenders]: array size is [0], but cannot be smaller than [1]"`
|
||||
);
|
||||
});
|
||||
|
||||
test('`schema` throws if `root` logger does not have "default" appender configured.', () => {
|
||||
|
@ -29,7 +59,9 @@ test('`schema` throws if `root` logger does not have "default" appender configur
|
|||
appenders: ['console'],
|
||||
},
|
||||
})
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[root]: \\"default\\" appender required for migration period till the next major release"`
|
||||
);
|
||||
});
|
||||
|
||||
test('`getParentLoggerContext()` returns correct parent context name.', () => {
|
||||
|
@ -157,7 +189,9 @@ test('fails if loggers use unknown appenders.', () => {
|
|||
],
|
||||
});
|
||||
|
||||
expect(() => new LoggingConfig(validateConfig)).toThrowErrorMatchingSnapshot();
|
||||
expect(() => new LoggingConfig(validateConfig)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Logger \\"some.nested.context\\" contains unsupported appender key \\"unknown\\"."`
|
||||
);
|
||||
});
|
||||
|
||||
describe('extend', () => {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { legacyLoggingConfigSchema } from '@kbn/legacy-logging';
|
||||
import { AppenderConfigType, Appenders } from './appenders/appenders';
|
||||
|
||||
// We need this helper for the types to be correct
|
||||
|
@ -59,7 +60,7 @@ export const loggerSchema = schema.object({
|
|||
export type LoggerConfigType = TypeOf<typeof loggerSchema>;
|
||||
export const config = {
|
||||
path: 'logging',
|
||||
schema: schema.object({
|
||||
schema: legacyLoggingConfigSchema.extends({
|
||||
appenders: schema.mapOf(schema.string(), Appenders.configSchema, {
|
||||
defaultValue: new Map<string, AppenderConfigType>(),
|
||||
}),
|
||||
|
@ -85,7 +86,7 @@ export const config = {
|
|||
}),
|
||||
};
|
||||
|
||||
export type LoggingConfigType = Omit<TypeOf<typeof config.schema>, 'appenders'> & {
|
||||
export type LoggingConfigType = Pick<TypeOf<typeof config.schema>, 'loggers' | 'root'> & {
|
||||
appenders: Map<string, AppenderConfigType>;
|
||||
};
|
||||
|
||||
|
@ -105,6 +106,7 @@ export const loggerContextConfigSchema = schema.object({
|
|||
|
||||
/** @public */
|
||||
export type LoggerContextConfigType = TypeOf<typeof loggerContextConfigSchema>;
|
||||
|
||||
/** @public */
|
||||
export interface LoggerContextConfigInput {
|
||||
// config-schema knows how to handle either Maps or Records
|
||||
|
|
|
@ -16,6 +16,7 @@ jest.mock('fs', () => ({
|
|||
const dynamicProps = { process: { pid: expect.any(Number) } };
|
||||
|
||||
jest.mock('@kbn/legacy-logging', () => ({
|
||||
...(jest.requireActual('@kbn/legacy-logging') as any),
|
||||
setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})),
|
||||
}));
|
||||
|
||||
|
|
|
@ -16,3 +16,4 @@ export type {
|
|||
export type { OpsProcessMetrics, OpsServerMetrics, OpsOsMetrics } from './collectors';
|
||||
export { MetricsService } from './metrics_service';
|
||||
export { opsConfig } from './ops_config';
|
||||
export type { OpsConfigType } from './ops_config';
|
||||
|
|
|
@ -13,7 +13,7 @@ import { getGlobalConfig, getGlobalConfig$ } from './legacy_config';
|
|||
import { REPO_ROOT } from '@kbn/utils';
|
||||
import { loggingSystemMock } from '../logging/logging_system.mock';
|
||||
import { duration } from 'moment';
|
||||
import { fromRoot } from '../utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { Server } from '../server';
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { duration } from 'moment';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { REPO_ROOT } from '@kbn/dev-utils';
|
||||
import { fromRoot } from '@kbn/utils';
|
||||
import { createPluginInitializerContext, InstanceInfo } from './plugin_context';
|
||||
import { CoreContext } from '../core_context';
|
||||
import { Env } from '../config';
|
||||
|
@ -16,7 +17,6 @@ import { loggingSystemMock } from '../logging/logging_system.mock';
|
|||
import { rawConfigServiceMock, getEnvOptions } from '../config/mocks';
|
||||
import { PluginManifest } from './types';
|
||||
import { Server } from '../server';
|
||||
import { fromRoot } from '../utils';
|
||||
import { schema, ByteSizeValue } from '@kbn/config-schema';
|
||||
import { ConfigService } from '@kbn/config';
|
||||
|
||||
|
|
|
@ -7,20 +7,24 @@
|
|||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { ServiceConfigDescriptor } from '../internal_types';
|
||||
import { Env } from '../config';
|
||||
|
||||
export type PluginsConfigType = TypeOf<typeof config.schema>;
|
||||
const configSchema = schema.object({
|
||||
initialize: schema.boolean({ defaultValue: true }),
|
||||
|
||||
export const config = {
|
||||
/**
|
||||
* Defines an array of directories where another plugin should be loaded from.
|
||||
*/
|
||||
paths: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||
});
|
||||
|
||||
export type PluginsConfigType = TypeOf<typeof configSchema>;
|
||||
|
||||
export const config: ServiceConfigDescriptor<PluginsConfigType> = {
|
||||
path: 'plugins',
|
||||
schema: schema.object({
|
||||
initialize: schema.boolean({ defaultValue: true }),
|
||||
|
||||
/**
|
||||
* Defines an array of directories where another plugin should be loaded from.
|
||||
*/
|
||||
paths: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||
}),
|
||||
schema: configSchema,
|
||||
deprecations: ({ unusedFromRoot }) => [unusedFromRoot('plugins.scanDirs')],
|
||||
};
|
||||
|
||||
/** @internal */
|
||||
|
|
|
@ -142,7 +142,6 @@ import { SearchParams } from 'elasticsearch';
|
|||
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
|
||||
import { SearchShardsParams } from 'elasticsearch';
|
||||
import { SearchTemplateParams } from 'elasticsearch';
|
||||
import { Server } from '@hapi/hapi';
|
||||
import { ShallowPromise } from '@kbn/utility-types';
|
||||
import { SnapshotCreateParams } from 'elasticsearch';
|
||||
import { SnapshotCreateRepositoryParams } from 'elasticsearch';
|
||||
|
@ -345,7 +344,7 @@ export const config: {
|
|||
pingTimeout: Type<import("moment").Duration>;
|
||||
logQueries: Type<boolean>;
|
||||
ssl: import("@kbn/config-schema").ObjectType<{
|
||||
verificationMode: Type<"certificate" | "none" | "full">;
|
||||
verificationMode: Type<"none" | "certificate" | "full">;
|
||||
certificateAuthorities: Type<string | string[] | undefined>;
|
||||
certificate: Type<string | undefined>;
|
||||
key: Type<string | undefined>;
|
||||
|
@ -1305,10 +1304,10 @@ export type KibanaResponseFactory = typeof kibanaResponseFactory;
|
|||
|
||||
// @public
|
||||
export const kibanaResponseFactory: {
|
||||
custom: <T extends string | Record<string, any> | Error | Buffer | {
|
||||
custom: <T extends string | Record<string, any> | Error | Buffer | Stream | {
|
||||
message: string | Error;
|
||||
attributes?: Record<string, any> | undefined;
|
||||
} | Stream | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||
} | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||
|
@ -1585,20 +1584,6 @@ export class LegacyClusterClient implements ILegacyClusterClient {
|
|||
close(): void;
|
||||
}
|
||||
|
||||
// @internal @deprecated
|
||||
export interface LegacyConfig {
|
||||
// (undocumented)
|
||||
get<T>(key?: string): T;
|
||||
// (undocumented)
|
||||
has(key: string): boolean;
|
||||
// (undocumented)
|
||||
set(key: string, value: any): void;
|
||||
// Warning: (ae-forgotten-export) The symbol "LegacyVars" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
set(config: LegacyVars): void;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type LegacyElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log' | 'plugins'> & Pick<ElasticsearchConfig, 'apiVersion' | 'customHeaders' | 'requestHeadersWhitelist' | 'sniffOnStart' | 'sniffOnConnectionFault' | 'hosts' | 'username' | 'password'> & {
|
||||
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
|
||||
|
@ -1634,30 +1619,6 @@ export class LegacyScopedClusterClient implements ILegacyScopedClusterClient {
|
|||
callAsInternalUser(endpoint: string, clientParams?: Record<string, any>, options?: LegacyCallAPIOptions): Promise<any>;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export interface LegacyServiceSetupDeps {
|
||||
// Warning: (ae-forgotten-export) The symbol "LegacyCoreSetup" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
core: LegacyCoreSetup;
|
||||
// (undocumented)
|
||||
plugins: Record<string, unknown>;
|
||||
// Warning: (ae-forgotten-export) The symbol "UiPlugins" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
uiPlugins: UiPlugins;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export interface LegacyServiceStartDeps {
|
||||
// Warning: (ae-forgotten-export) The symbol "LegacyCoreStart" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// (undocumented)
|
||||
core: LegacyCoreStart;
|
||||
// (undocumented)
|
||||
plugins: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @public
|
||||
|
|
|
@ -58,7 +58,7 @@ jest.doMock('./ui_settings/ui_settings_service', () => ({
|
|||
}));
|
||||
|
||||
export const mockEnsureValidConfiguration = jest.fn();
|
||||
jest.doMock('./legacy/config/ensure_valid_configuration', () => ({
|
||||
jest.doMock('./config/ensure_valid_configuration', () => ({
|
||||
ensureValidConfiguration: mockEnsureValidConfiguration,
|
||||
}));
|
||||
|
||||
|
|
|
@ -99,7 +99,6 @@ test('injects legacy dependency to context#setup()', async () => {
|
|||
pluginDependencies: new Map([
|
||||
[pluginA, []],
|
||||
[pluginB, [pluginA]],
|
||||
[mockLegacyService.legacyId, [pluginA, pluginB]],
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
@ -108,12 +107,10 @@ test('runs services on "start"', async () => {
|
|||
const server = new Server(rawConfigService, env, logger);
|
||||
|
||||
expect(mockHttpService.setup).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
||||
|
||||
await server.setup();
|
||||
|
||||
expect(mockHttpService.start).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
||||
expect(mockSavedObjectsService.start).not.toHaveBeenCalled();
|
||||
expect(mockUiSettingsService.start).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.start).not.toHaveBeenCalled();
|
||||
|
@ -121,7 +118,6 @@ test('runs services on "start"', async () => {
|
|||
await server.start();
|
||||
|
||||
expect(mockHttpService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1);
|
||||
expect(mockMetricsService.start).toHaveBeenCalledTimes(1);
|
||||
|
@ -164,26 +160,6 @@ test('stops services on "stop"', async () => {
|
|||
});
|
||||
|
||||
test(`doesn't setup core services if config validation fails`, async () => {
|
||||
mockConfigService.validate.mockImplementationOnce(() => {
|
||||
return Promise.reject(new Error('invalid config'));
|
||||
});
|
||||
const server = new Server(rawConfigService, env, logger);
|
||||
await expect(server.setup()).rejects.toThrowErrorMatchingInlineSnapshot(`"invalid config"`);
|
||||
|
||||
expect(mockHttpService.setup).not.toHaveBeenCalled();
|
||||
expect(mockElasticsearchService.setup).not.toHaveBeenCalled();
|
||||
expect(mockPluginsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockLegacyService.setup).not.toHaveBeenCalled();
|
||||
expect(mockSavedObjectsService.stop).not.toHaveBeenCalled();
|
||||
expect(mockUiSettingsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockRenderingService.setup).not.toHaveBeenCalled();
|
||||
expect(mockMetricsService.setup).not.toHaveBeenCalled();
|
||||
expect(mockStatusService.setup).not.toHaveBeenCalled();
|
||||
expect(mockLoggingService.setup).not.toHaveBeenCalled();
|
||||
expect(mockI18nService.setup).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test(`doesn't setup core services if legacy config validation fails`, async () => {
|
||||
mockEnsureValidConfiguration.mockImplementation(() => {
|
||||
throw new Error('Unknown configuration keys');
|
||||
});
|
||||
|
|
|
@ -8,15 +8,20 @@
|
|||
|
||||
import apm from 'elastic-apm-node';
|
||||
import { config as pathConfig } from '@kbn/utils';
|
||||
import { mapToObject } from '@kbn/std';
|
||||
import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config';
|
||||
import {
|
||||
ConfigService,
|
||||
Env,
|
||||
RawConfigurationProvider,
|
||||
coreDeprecationProvider,
|
||||
ensureValidConfiguration,
|
||||
} from './config';
|
||||
import { CoreApp } from './core_app';
|
||||
import { I18nService } from './i18n';
|
||||
import { ElasticsearchService } from './elasticsearch';
|
||||
import { HttpService } from './http';
|
||||
import { HttpResourcesService } from './http_resources';
|
||||
import { RenderingService } from './rendering';
|
||||
import { LegacyService, ensureValidConfiguration } from './legacy';
|
||||
import { LegacyService } from './legacy';
|
||||
import { Logger, LoggerFactory, LoggingService, ILoggingSystem } from './logging';
|
||||
import { UiSettingsService } from './ui_settings';
|
||||
import { PluginsService, config as pluginsConfig } from './plugins';
|
||||
|
@ -121,22 +126,13 @@ export class Server {
|
|||
const { pluginTree, pluginPaths, uiPlugins } = await this.plugins.discover({
|
||||
environment: environmentSetup,
|
||||
});
|
||||
const legacyConfigSetup = await this.legacy.setupLegacyConfig();
|
||||
|
||||
// Immediately terminate in case of invalid configuration
|
||||
// This needs to be done after plugin discovery
|
||||
await this.configService.validate();
|
||||
await ensureValidConfiguration(this.configService, legacyConfigSetup);
|
||||
await ensureValidConfiguration(this.configService);
|
||||
|
||||
const contextServiceSetup = this.context.setup({
|
||||
// We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins:
|
||||
// 1) Can access context from any KP plugin
|
||||
// 2) Can register context providers that will only be available to other legacy plugins and will not leak into
|
||||
// New Platform plugins.
|
||||
pluginDependencies: new Map([
|
||||
...pluginTree.asOpaqueIds,
|
||||
[this.legacy.legacyId, [...pluginTree.asOpaqueIds.keys()]],
|
||||
]),
|
||||
pluginDependencies: new Map([...pluginTree.asOpaqueIds]),
|
||||
});
|
||||
|
||||
const httpSetup = await this.http.setup({
|
||||
|
@ -222,9 +218,7 @@ export class Server {
|
|||
this.#pluginsInitialized = pluginsSetup.initialized;
|
||||
|
||||
await this.legacy.setup({
|
||||
core: { ...coreSetup, plugins: pluginsSetup, rendering: renderingSetup },
|
||||
plugins: mapToObject(pluginsSetup.contracts),
|
||||
uiPlugins,
|
||||
http: httpSetup,
|
||||
});
|
||||
|
||||
this.registerCoreContext(coreSetup);
|
||||
|
@ -266,15 +260,7 @@ export class Server {
|
|||
coreUsageData: coreUsageDataStart,
|
||||
};
|
||||
|
||||
const pluginsStart = await this.plugins.start(this.coreStart);
|
||||
|
||||
await this.legacy.start({
|
||||
core: {
|
||||
...this.coreStart,
|
||||
plugins: pluginsStart,
|
||||
},
|
||||
plugins: mapToObject(pluginsStart.contracts),
|
||||
});
|
||||
await this.plugins.start(this.coreStart);
|
||||
|
||||
await this.http.start();
|
||||
|
||||
|
|
|
@ -39,6 +39,5 @@ export type {
|
|||
} from './saved_objects/types';
|
||||
export type { DomainDeprecationDetails, DeprecationsGetResponse } from './deprecations/types';
|
||||
export * from './ui_settings/types';
|
||||
export * from './legacy/types';
|
||||
export type { EnvironmentMode, PackageInfo } from '@kbn/config';
|
||||
export type { ExternalUrlConfig, IExternalUrlPolicy } from './external_url';
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
import { getServices, chance } from './lib';
|
||||
|
||||
export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||
async function setup(options: any = {}) {
|
||||
async function setup(options: { initialSettings?: Record<string, any> } = {}) {
|
||||
const { initialSettings } = options;
|
||||
|
||||
const { kbnServer, uiSettings, callCluster } = getServices();
|
||||
const { uiSettings, callCluster, supertest } = getServices();
|
||||
|
||||
// delete the kibana index to ensure we start fresh
|
||||
await callCluster('deleteByQuery', {
|
||||
|
@ -21,31 +21,30 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
conflicts: 'proceed',
|
||||
query: { match_all: {} },
|
||||
},
|
||||
refresh: true,
|
||||
wait_for_completion: true,
|
||||
});
|
||||
|
||||
if (initialSettings) {
|
||||
await uiSettings.setMany(initialSettings);
|
||||
}
|
||||
|
||||
return { kbnServer, uiSettings };
|
||||
return { uiSettings, supertest };
|
||||
}
|
||||
|
||||
describe('get route', () => {
|
||||
it('returns a 200 and includes userValues', async () => {
|
||||
const defaultIndex = chance.word({ length: 10 });
|
||||
const { kbnServer } = await setup({
|
||||
|
||||
const { supertest } = await setup({
|
||||
initialSettings: {
|
||||
defaultIndex,
|
||||
},
|
||||
});
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'GET',
|
||||
url: '/api/kibana/settings',
|
||||
});
|
||||
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -64,20 +63,17 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('set route', () => {
|
||||
it('returns a 200 and all values including update', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
const { supertest } = await setup();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
payload: {
|
||||
|
||||
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
|
||||
.send({
|
||||
value: defaultIndex,
|
||||
},
|
||||
});
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -94,18 +90,15 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
});
|
||||
|
||||
it('returns a 400 if trying to set overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
const { supertest } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/foo',
|
||||
payload: {
|
||||
const { body } = await supertest('delete', '/api/kibana/settings/foo')
|
||||
.send({
|
||||
value: 'baz',
|
||||
},
|
||||
});
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(statusCode).toBe(400);
|
||||
expect(result).toEqual({
|
||||
expect(body).toEqual({
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400,
|
||||
|
@ -115,22 +108,18 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('setMany route', () => {
|
||||
it('returns a 200 and all values including updates', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
const { supertest } = await setup();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings',
|
||||
payload: {
|
||||
const { body } = await supertest('post', '/api/kibana/settings')
|
||||
.send({
|
||||
changes: {
|
||||
defaultIndex,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -147,20 +136,17 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
});
|
||||
|
||||
it('returns a 400 if trying to set overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
const { supertest } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings',
|
||||
payload: {
|
||||
const { body } = await supertest('post', '/api/kibana/settings')
|
||||
.send({
|
||||
changes: {
|
||||
foo: 'baz',
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(statusCode).toBe(400);
|
||||
expect(result).toEqual({
|
||||
expect(body).toEqual({
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400,
|
||||
|
@ -172,19 +158,15 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
it('returns a 200 and deletes the setting', async () => {
|
||||
const defaultIndex = chance.word({ length: 10 });
|
||||
|
||||
const { kbnServer, uiSettings } = await setup({
|
||||
const { uiSettings, supertest } = await setup({
|
||||
initialSettings: { defaultIndex },
|
||||
});
|
||||
|
||||
expect(await uiSettings.get('defaultIndex')).toBe(defaultIndex);
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
});
|
||||
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -197,15 +179,11 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
|||
});
|
||||
});
|
||||
it('returns a 400 if deleting overridden value', async () => {
|
||||
const { kbnServer } = await setup();
|
||||
const { supertest } = await setup();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/kibana/settings/foo',
|
||||
});
|
||||
const { body } = await supertest('delete', '/api/kibana/settings/foo').expect(400);
|
||||
|
||||
expect(statusCode).toBe(400);
|
||||
expect(result).toEqual({
|
||||
expect(body).toEqual({
|
||||
error: 'Bad Request',
|
||||
message: 'Unable to update "foo" because it is overridden',
|
||||
statusCode: 400,
|
||||
|
|
|
@ -11,14 +11,7 @@ import { getServices, chance } from './lib';
|
|||
export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||
// ensure the kibana index has no documents
|
||||
beforeEach(async () => {
|
||||
const { kbnServer, callCluster } = getServices();
|
||||
|
||||
// write a setting to ensure kibana index is created
|
||||
await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
payload: { value: 'abc' },
|
||||
});
|
||||
const { callCluster } = getServices();
|
||||
|
||||
// delete all docs from kibana index to ensure savedConfig is not found
|
||||
await callCluster('deleteByQuery', {
|
||||
|
@ -31,15 +24,11 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('get route', () => {
|
||||
it('creates doc, returns a 200 with settings', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
const { supertest } = getServices();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'GET',
|
||||
url: '/api/kibana/settings',
|
||||
});
|
||||
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -55,17 +44,17 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('set route', () => {
|
||||
it('creates doc, returns a 200 with value set', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
const { supertest } = getServices();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
payload: { value: defaultIndex },
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
|
||||
.send({
|
||||
value: defaultIndex,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -84,19 +73,17 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('setMany route', () => {
|
||||
it('creates doc, returns 200 with updated values', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
const { supertest } = getServices();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings',
|
||||
payload: {
|
||||
changes: { defaultIndex },
|
||||
},
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
const { body } = await supertest('post', '/api/kibana/settings')
|
||||
.send({
|
||||
changes: { defaultIndex },
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
@ -115,15 +102,11 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
|||
|
||||
describe('delete route', () => {
|
||||
it('creates doc, returns a 200 with just buildNum', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
const { supertest } = getServices();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
});
|
||||
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
expect(result).toMatchObject({
|
||||
expect(body).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { getServices, chance } from './lib';
|
||||
|
||||
export const docMissingAndIndexReadOnlySuite = (savedObjectsIndex: string) => () => {
|
||||
// ensure the kibana index has no documents
|
||||
beforeEach(async () => {
|
||||
const { kbnServer, callCluster } = getServices();
|
||||
|
||||
// write a setting to ensure kibana index is created
|
||||
await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
payload: { value: 'abc' },
|
||||
});
|
||||
|
||||
// delete all docs from kibana index to ensure savedConfig is not found
|
||||
await callCluster('deleteByQuery', {
|
||||
index: savedObjectsIndex,
|
||||
body: {
|
||||
query: { match_all: {} },
|
||||
},
|
||||
});
|
||||
|
||||
// set the index to read only
|
||||
await callCluster('indices.putSettings', {
|
||||
index: savedObjectsIndex,
|
||||
body: {
|
||||
index: {
|
||||
blocks: {
|
||||
read_only: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const { callCluster } = getServices();
|
||||
|
||||
// disable the read only block
|
||||
await callCluster('indices.putSettings', {
|
||||
index: savedObjectsIndex,
|
||||
body: {
|
||||
index: {
|
||||
blocks: {
|
||||
read_only: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('get route', () => {
|
||||
it('returns simulated doc with buildNum', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'GET',
|
||||
url: '/api/kibana/settings',
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(200);
|
||||
|
||||
expect(result).toMatchObject({
|
||||
settings: {
|
||||
buildNum: {
|
||||
userValue: expect.any(Number),
|
||||
},
|
||||
foo: {
|
||||
userValue: 'bar',
|
||||
isOverridden: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('set route', () => {
|
||||
it('fails with 403 forbidden', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
payload: { value: defaultIndex },
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(403);
|
||||
|
||||
expect(result).toEqual({
|
||||
error: 'Forbidden',
|
||||
message: expect.stringContaining('index read-only'),
|
||||
statusCode: 403,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setMany route', () => {
|
||||
it('fails with 403 forbidden', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
|
||||
const defaultIndex = chance.word();
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'POST',
|
||||
url: '/api/kibana/settings',
|
||||
payload: {
|
||||
changes: { defaultIndex },
|
||||
},
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(403);
|
||||
expect(result).toEqual({
|
||||
error: 'Forbidden',
|
||||
message: expect.stringContaining('index read-only'),
|
||||
statusCode: 403,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete route', () => {
|
||||
it('fails with 403 forbidden', async () => {
|
||||
const { kbnServer } = getServices();
|
||||
|
||||
const { statusCode, result } = await kbnServer.inject({
|
||||
method: 'DELETE',
|
||||
url: '/api/kibana/settings/defaultIndex',
|
||||
});
|
||||
|
||||
expect(statusCode).toBe(403);
|
||||
expect(result).toEqual({
|
||||
error: 'Forbidden',
|
||||
message: expect.stringContaining('index read-only'),
|
||||
statusCode: 403,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -12,7 +12,6 @@ import { getEnvOptions } from '@kbn/config/target/mocks';
|
|||
import { startServers, stopServers } from './lib';
|
||||
import { docExistsSuite } from './doc_exists';
|
||||
import { docMissingSuite } from './doc_missing';
|
||||
import { docMissingAndIndexReadOnlySuite } from './doc_missing_and_index_read_only';
|
||||
|
||||
const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
|
||||
const savedObjectIndex = `.kibana_${kibanaVersion}_001`;
|
||||
|
@ -23,7 +22,6 @@ describe('uiSettings/routes', function () {
|
|||
beforeAll(startServers);
|
||||
/* eslint-disable jest/valid-describe */
|
||||
describe('doc missing', docMissingSuite(savedObjectIndex));
|
||||
describe('doc missing and index readonly', docMissingAndIndexReadOnlySuite(savedObjectIndex));
|
||||
describe('doc exists', docExistsSuite(savedObjectIndex));
|
||||
/* eslint-enable jest/valid-describe */
|
||||
afterAll(stopServers);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type supertest from 'supertest';
|
||||
import { SavedObjectsClientContract, IUiSettingsClient } from 'src/core/server';
|
||||
|
||||
import {
|
||||
|
@ -13,6 +14,8 @@ import {
|
|||
TestElasticsearchUtils,
|
||||
TestKibanaUtils,
|
||||
TestUtils,
|
||||
HttpMethod,
|
||||
getSupertest,
|
||||
} from '../../../../test_helpers/kbn_server';
|
||||
import { LegacyAPICaller } from '../../../elasticsearch/';
|
||||
import { httpServerMock } from '../../../http/http_server.mocks';
|
||||
|
@ -21,13 +24,11 @@ let servers: TestUtils;
|
|||
let esServer: TestElasticsearchUtils;
|
||||
let kbn: TestKibanaUtils;
|
||||
|
||||
let kbnServer: TestKibanaUtils['kbnServer'];
|
||||
|
||||
interface AllServices {
|
||||
kbnServer: TestKibanaUtils['kbnServer'];
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
callCluster: LegacyAPICaller;
|
||||
uiSettings: IUiSettingsClient;
|
||||
supertest: (method: HttpMethod, path: string) => supertest.Test;
|
||||
}
|
||||
|
||||
let services: AllServices;
|
||||
|
@ -47,7 +48,6 @@ export async function startServers() {
|
|||
});
|
||||
esServer = await servers.startES();
|
||||
kbn = await servers.startKibana();
|
||||
kbnServer = kbn.kbnServer;
|
||||
}
|
||||
|
||||
export function getServices() {
|
||||
|
@ -61,12 +61,10 @@ export function getServices() {
|
|||
httpServerMock.createKibanaRequest()
|
||||
);
|
||||
|
||||
const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient(
|
||||
savedObjectsClient
|
||||
);
|
||||
const uiSettings = kbn.coreStart.uiSettings.asScopedToClient(savedObjectsClient);
|
||||
|
||||
services = {
|
||||
kbnServer,
|
||||
supertest: (method: HttpMethod, path: string) => getSupertest(kbn.root, method, path),
|
||||
callCluster,
|
||||
savedObjectsClient,
|
||||
uiSettings,
|
||||
|
@ -77,7 +75,6 @@ export function getServices() {
|
|||
|
||||
export async function stopServers() {
|
||||
services = null!;
|
||||
kbnServer = null!;
|
||||
if (servers) {
|
||||
await esServer.stop();
|
||||
await kbn.stop();
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import { pkg } from './package_json';
|
||||
|
||||
export function fromRoot(...args: string[]) {
|
||||
return resolve(pkg.__dirname, ...args);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './from_root';
|
||||
export * from './package_json';
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { dirname } from 'path';
|
||||
|
||||
export const pkg = {
|
||||
__filename: require.resolve('../../../../package.json'),
|
||||
__dirname: dirname(require.resolve('../../../../package.json')),
|
||||
...require('../../../../package.json'),
|
||||
};
|
|
@ -29,11 +29,10 @@ import { resolve } from 'path';
|
|||
import { BehaviorSubject } from 'rxjs';
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { CoreStart } from 'src/core/server';
|
||||
import { InternalCoreSetup, InternalCoreStart } from '../server/internal_types';
|
||||
import { LegacyAPICaller } from '../server/elasticsearch';
|
||||
import { CliArgs, Env } from '../server/config';
|
||||
import { Root } from '../server/root';
|
||||
import KbnServer from '../../legacy/server/kbn_server';
|
||||
|
||||
export type HttpMethod = 'delete' | 'get' | 'head' | 'post' | 'put';
|
||||
|
||||
|
@ -125,14 +124,6 @@ export function createRootWithCorePlugins(settings = {}, cliArgs: Partial<CliArg
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `kbnServer` instance used in the "legacy" Kibana.
|
||||
* @param root
|
||||
*/
|
||||
export function getKbnServer(root: Root): KbnServer {
|
||||
return (root as any).server.legacy.kbnServer;
|
||||
}
|
||||
|
||||
export const request: Record<
|
||||
HttpMethod,
|
||||
(root: Root, path: string) => ReturnType<typeof getSupertest>
|
||||
|
@ -164,8 +155,8 @@ export interface TestElasticsearchUtils {
|
|||
|
||||
export interface TestKibanaUtils {
|
||||
root: Root;
|
||||
coreStart: CoreStart;
|
||||
kbnServer: KbnServer;
|
||||
coreSetup: InternalCoreSetup;
|
||||
coreStart: InternalCoreStart;
|
||||
stop: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
@ -283,14 +274,12 @@ export function createTestServers({
|
|||
startKibana: async () => {
|
||||
const root = createRootWithCorePlugins(kbnSettings);
|
||||
|
||||
await root.setup();
|
||||
const coreSetup = await root.setup();
|
||||
const coreStart = await root.start();
|
||||
|
||||
const kbnServer = getKbnServer(root);
|
||||
|
||||
return {
|
||||
root,
|
||||
kbnServer,
|
||||
coreSetup,
|
||||
coreStart,
|
||||
stop: async () => await root.shutdown(),
|
||||
};
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`lib/config/config class Config() #getDefault(key) array key should throw exception for unknown key 1`] = `"Unknown config key: foo,bar."`;
|
||||
|
||||
exports[`lib/config/config class Config() #getDefault(key) dot notation key should throw exception for unknown key 1`] = `"Unknown config key: foo.bar."`;
|
|
@ -1,207 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
import { set } from '@elastic/safer-lodash-set';
|
||||
import _ from 'lodash';
|
||||
import { override } from './override';
|
||||
import createDefaultSchema from './schema';
|
||||
import { unset, deepCloneWithBuffers as clone, IS_KIBANA_DISTRIBUTABLE } from '../../utils';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { pkg } from '../../../core/server/utils';
|
||||
const schema = Symbol('Joi Schema');
|
||||
const schemaExts = Symbol('Schema Extensions');
|
||||
const vals = Symbol('config values');
|
||||
|
||||
export class Config {
|
||||
static withDefaultSchema(settings = {}) {
|
||||
const defaultSchema = createDefaultSchema();
|
||||
return new Config(defaultSchema, settings);
|
||||
}
|
||||
|
||||
constructor(initialSchema, initialSettings) {
|
||||
this[schemaExts] = Object.create(null);
|
||||
this[vals] = Object.create(null);
|
||||
|
||||
this.extendSchema(initialSchema, initialSettings);
|
||||
}
|
||||
|
||||
extendSchema(extension, settings, key) {
|
||||
if (!extension) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
return _.each(extension._inner.children, (child) => {
|
||||
this.extendSchema(child.schema, _.get(settings, child.key), child.key);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.has(key)) {
|
||||
throw new Error(`Config schema already has key: ${key}`);
|
||||
}
|
||||
|
||||
set(this[schemaExts], key, extension);
|
||||
this[schema] = null;
|
||||
|
||||
this.set(key, settings);
|
||||
}
|
||||
|
||||
removeSchema(key) {
|
||||
if (!_.has(this[schemaExts], key)) {
|
||||
throw new TypeError(`Unknown schema key: ${key}`);
|
||||
}
|
||||
|
||||
this[schema] = null;
|
||||
unset(this[schemaExts], key);
|
||||
unset(this[vals], key);
|
||||
}
|
||||
|
||||
resetTo(obj) {
|
||||
this._commit(obj);
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
// clone and modify the config
|
||||
let config = clone(this[vals]);
|
||||
if (_.isPlainObject(key)) {
|
||||
config = override(config, key);
|
||||
} else {
|
||||
set(config, key, value);
|
||||
}
|
||||
|
||||
// attempt to validate the config value
|
||||
this._commit(config);
|
||||
}
|
||||
|
||||
_commit(newVals) {
|
||||
// resolve the current environment
|
||||
let env = newVals.env;
|
||||
delete newVals.env;
|
||||
if (_.isObject(env)) env = env.name;
|
||||
if (!env) env = 'production';
|
||||
|
||||
const dev = env === 'development';
|
||||
const prod = env === 'production';
|
||||
|
||||
// pass the environment as context so that it can be refed in config
|
||||
const context = {
|
||||
env: env,
|
||||
prod: prod,
|
||||
dev: dev,
|
||||
notProd: !prod,
|
||||
notDev: !dev,
|
||||
version: _.get(pkg, 'version'),
|
||||
branch: _.get(pkg, 'branch'),
|
||||
buildNum: IS_KIBANA_DISTRIBUTABLE ? pkg.build.number : Number.MAX_SAFE_INTEGER,
|
||||
buildSha: IS_KIBANA_DISTRIBUTABLE
|
||||
? pkg.build.sha
|
||||
: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
||||
dist: IS_KIBANA_DISTRIBUTABLE,
|
||||
};
|
||||
|
||||
if (!context.dev && !context.prod) {
|
||||
throw new TypeError(
|
||||
`Unexpected environment "${env}", expected one of "development" or "production"`
|
||||
);
|
||||
}
|
||||
|
||||
const results = Joi.validate(newVals, this.getSchema(), {
|
||||
context,
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (results.error) {
|
||||
const error = new Error(results.error.message);
|
||||
error.name = results.error.name;
|
||||
error.stack = results.error.stack;
|
||||
throw error;
|
||||
}
|
||||
|
||||
this[vals] = results.value;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (!key) {
|
||||
return clone(this[vals]);
|
||||
}
|
||||
|
||||
const value = _.get(this[vals], key);
|
||||
if (value === undefined) {
|
||||
if (!this.has(key)) {
|
||||
throw new Error('Unknown config key: ' + key);
|
||||
}
|
||||
}
|
||||
return clone(value);
|
||||
}
|
||||
|
||||
getDefault(key) {
|
||||
const schemaKey = Array.isArray(key) ? key.join('.') : key;
|
||||
|
||||
const subSchema = Joi.reach(this.getSchema(), schemaKey);
|
||||
if (!subSchema) {
|
||||
throw new Error(`Unknown config key: ${key}.`);
|
||||
}
|
||||
|
||||
return clone(_.get(Joi.describe(subSchema), 'flags.default'));
|
||||
}
|
||||
|
||||
has(key) {
|
||||
function has(key, schema, path) {
|
||||
path = path || [];
|
||||
// Catch the partial paths
|
||||
if (path.join('.') === key) return true;
|
||||
// Only go deep on inner objects with children
|
||||
if (_.size(schema._inner.children)) {
|
||||
for (let i = 0; i < schema._inner.children.length; i++) {
|
||||
const child = schema._inner.children[i];
|
||||
// If the child is an object recurse through it's children and return
|
||||
// true if there's a match
|
||||
if (child.schema._type === 'object') {
|
||||
if (has(key, child.schema, path.concat([child.key]))) return true;
|
||||
// if the child matches, return true
|
||||
} else if (path.concat([child.key]).join('.') === key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(key)) {
|
||||
// TODO: add .has() support for array keys
|
||||
key = key.join('.');
|
||||
}
|
||||
|
||||
return !!has(key, this.getSchema());
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
if (!this[schema]) {
|
||||
this[schema] = (function convertToSchema(children) {
|
||||
let schema = Joi.object().keys({}).default();
|
||||
|
||||
for (const key of Object.keys(children)) {
|
||||
const child = children[key];
|
||||
const childSchema = _.isPlainObject(child) ? convertToSchema(child) : child;
|
||||
|
||||
if (!childSchema || !childSchema.isJoi) {
|
||||
throw new TypeError(
|
||||
'Unable to convert configuration definition value to Joi schema: ' + childSchema
|
||||
);
|
||||
}
|
||||
|
||||
schema = schema.keys({ [key]: childSchema });
|
||||
}
|
||||
|
||||
return schema;
|
||||
})(this[schemaExts]);
|
||||
}
|
||||
|
||||
return this[schema];
|
||||
}
|
||||
}
|
|
@ -1,345 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Config } from './config';
|
||||
import _ from 'lodash';
|
||||
import Joi from 'joi';
|
||||
|
||||
/**
|
||||
* Plugins should defined a config method that takes a joi object. By default
|
||||
* it should return a way to disallow config
|
||||
*
|
||||
* Config should be newed up with a joi schema (containing defaults via joi)
|
||||
*
|
||||
* let schema = { ... }
|
||||
* new Config(schema);
|
||||
*
|
||||
*/
|
||||
|
||||
const data = {
|
||||
test: {
|
||||
hosts: ['host-01', 'host-02'],
|
||||
client: {
|
||||
type: 'datastore',
|
||||
host: 'store-01',
|
||||
port: 5050,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const schema = Joi.object({
|
||||
test: Joi.object({
|
||||
enable: Joi.boolean().default(true),
|
||||
hosts: Joi.array().items(Joi.string()),
|
||||
client: Joi.object({
|
||||
type: Joi.string().default('datastore'),
|
||||
host: Joi.string(),
|
||||
port: Joi.number(),
|
||||
}).default(),
|
||||
undefValue: Joi.string(),
|
||||
}).default(),
|
||||
}).default();
|
||||
|
||||
describe('lib/config/config', function () {
|
||||
describe('class Config()', function () {
|
||||
describe('constructor', function () {
|
||||
it('should not allow any config if the schema is not passed', function () {
|
||||
const config = new Config();
|
||||
const run = function () {
|
||||
config.set('something.enable', true);
|
||||
};
|
||||
expect(run).toThrow();
|
||||
});
|
||||
|
||||
it('should allow keys in the schema', function () {
|
||||
const config = new Config(schema);
|
||||
const run = function () {
|
||||
config.set('test.client.host', 'http://localhost');
|
||||
};
|
||||
expect(run).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not allow keys not in the schema', function () {
|
||||
const config = new Config(schema);
|
||||
const run = function () {
|
||||
config.set('paramNotDefinedInTheSchema', true);
|
||||
};
|
||||
expect(run).toThrow();
|
||||
});
|
||||
|
||||
it('should not allow child keys not in the schema', function () {
|
||||
const config = new Config(schema);
|
||||
const run = function () {
|
||||
config.set('test.client.paramNotDefinedInTheSchema', true);
|
||||
};
|
||||
expect(run).toThrow();
|
||||
});
|
||||
|
||||
it('should set defaults', function () {
|
||||
const config = new Config(schema);
|
||||
expect(config.get('test.enable')).toBe(true);
|
||||
expect(config.get('test.client.type')).toBe('datastore');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resetTo(object)', function () {
|
||||
let config;
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
});
|
||||
|
||||
it('should reset the config object with new values', function () {
|
||||
config.set(data);
|
||||
const newData = config.get();
|
||||
newData.test.enable = false;
|
||||
config.resetTo(newData);
|
||||
expect(config.get()).toEqual(newData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#has(key)', function () {
|
||||
let config;
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
});
|
||||
|
||||
it('should return true for fields that exist in the schema', function () {
|
||||
expect(config.has('test.undefValue')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for partial objects that exist in the schema', function () {
|
||||
expect(config.has('test.client')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for fields that do not exist in the schema', function () {
|
||||
expect(config.has('test.client.pool')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set(key, value)', function () {
|
||||
let config;
|
||||
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
});
|
||||
|
||||
it('should use a key and value to set a config value', function () {
|
||||
config.set('test.enable', false);
|
||||
expect(config.get('test.enable')).toBe(false);
|
||||
});
|
||||
|
||||
it('should use an object to set config values', function () {
|
||||
const hosts = ['host-01', 'host-02'];
|
||||
config.set({ test: { enable: false, hosts: hosts } });
|
||||
expect(config.get('test.enable')).toBe(false);
|
||||
expect(config.get('test.hosts')).toEqual(hosts);
|
||||
});
|
||||
|
||||
it('should use a flatten object to set config values', function () {
|
||||
const hosts = ['host-01', 'host-02'];
|
||||
config.set({ 'test.enable': false, 'test.hosts': hosts });
|
||||
expect(config.get('test.enable')).toBe(false);
|
||||
expect(config.get('test.hosts')).toEqual(hosts);
|
||||
});
|
||||
|
||||
it('should override values with just the values present', function () {
|
||||
const newData = _.cloneDeep(data);
|
||||
config.set(data);
|
||||
newData.test.enable = false;
|
||||
config.set({ test: { enable: false } });
|
||||
expect(config.get()).toEqual(newData);
|
||||
});
|
||||
|
||||
it('should thow an exception when setting a value with the wrong type', function (done) {
|
||||
expect.assertions(4);
|
||||
|
||||
const run = function () {
|
||||
config.set('test.enable', 'something');
|
||||
};
|
||||
|
||||
try {
|
||||
run();
|
||||
} catch (err) {
|
||||
expect(err).toHaveProperty('name', 'ValidationError');
|
||||
expect(err).toHaveProperty(
|
||||
'message',
|
||||
'child "test" fails because [child "enable" fails because ["enable" must be a boolean]]'
|
||||
);
|
||||
expect(err).not.toHaveProperty('details');
|
||||
expect(err).not.toHaveProperty('_object');
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#get(key)', function () {
|
||||
let config;
|
||||
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
config.set(data);
|
||||
});
|
||||
|
||||
it('should return the whole config object when called without a key', function () {
|
||||
const newData = _.cloneDeep(data);
|
||||
newData.test.enable = true;
|
||||
expect(config.get()).toEqual(newData);
|
||||
});
|
||||
|
||||
it('should return the value using dot notation', function () {
|
||||
expect(config.get('test.enable')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return the clone of partial object using dot notation', function () {
|
||||
expect(config.get('test.client')).not.toBe(data.test.client);
|
||||
expect(config.get('test.client')).toEqual(data.test.client);
|
||||
});
|
||||
|
||||
it('should throw exception for unknown config values', function () {
|
||||
const run = function () {
|
||||
config.get('test.does.not.exist');
|
||||
};
|
||||
expect(run).toThrowError(/Unknown config key: test.does.not.exist/);
|
||||
});
|
||||
|
||||
it('should not throw exception for undefined known config values', function () {
|
||||
const run = function getUndefValue() {
|
||||
config.get('test.undefValue');
|
||||
};
|
||||
expect(run).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getDefault(key)', function () {
|
||||
let config;
|
||||
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
config.set(data);
|
||||
});
|
||||
|
||||
describe('dot notation key', function () {
|
||||
it('should return undefined if there is no default', function () {
|
||||
const hostDefault = config.getDefault('test.client.host');
|
||||
expect(hostDefault).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return default if specified', function () {
|
||||
const typeDefault = config.getDefault('test.client.type');
|
||||
expect(typeDefault).toBe('datastore');
|
||||
});
|
||||
|
||||
it('should throw exception for unknown key', function () {
|
||||
expect(() => {
|
||||
config.getDefault('foo.bar');
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('array key', function () {
|
||||
it('should return undefined if there is no default', function () {
|
||||
const hostDefault = config.getDefault(['test', 'client', 'host']);
|
||||
expect(hostDefault).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return default if specified', function () {
|
||||
const typeDefault = config.getDefault(['test', 'client', 'type']);
|
||||
expect(typeDefault).toBe('datastore');
|
||||
});
|
||||
|
||||
it('should throw exception for unknown key', function () {
|
||||
expect(() => {
|
||||
config.getDefault(['foo', 'bar']);
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('object schema with no default should return default value for property', function () {
|
||||
const noDefaultSchema = Joi.object()
|
||||
.keys({
|
||||
foo: Joi.array().items(Joi.string().min(1)).default(['bar']),
|
||||
})
|
||||
.required();
|
||||
|
||||
const config = new Config(noDefaultSchema);
|
||||
config.set({
|
||||
foo: ['baz'],
|
||||
});
|
||||
|
||||
const fooDefault = config.getDefault('foo');
|
||||
expect(fooDefault).toEqual(['bar']);
|
||||
});
|
||||
|
||||
it('should return clone of the default', function () {
|
||||
const schemaWithArrayDefault = Joi.object()
|
||||
.keys({
|
||||
foo: Joi.array().items(Joi.string().min(1)).default(['bar']),
|
||||
})
|
||||
.default();
|
||||
|
||||
const config = new Config(schemaWithArrayDefault);
|
||||
config.set({
|
||||
foo: ['baz'],
|
||||
});
|
||||
|
||||
expect(config.getDefault('foo')).not.toBe(config.getDefault('foo'));
|
||||
expect(config.getDefault('foo')).toEqual(config.getDefault('foo'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#extendSchema(key, schema)', function () {
|
||||
let config;
|
||||
beforeEach(function () {
|
||||
config = new Config(schema);
|
||||
});
|
||||
|
||||
it('should allow you to extend the schema at the top level', function () {
|
||||
const newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
|
||||
config.extendSchema(newSchema, {}, 'myTest');
|
||||
expect(config.get('myTest.test')).toBe(true);
|
||||
});
|
||||
|
||||
it('should allow you to extend the schema with a prefix', function () {
|
||||
const newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
|
||||
config.extendSchema(newSchema, {}, 'prefix.myTest');
|
||||
expect(config.get('prefix')).toEqual({ myTest: { test: true } });
|
||||
expect(config.get('prefix.myTest')).toEqual({ test: true });
|
||||
expect(config.get('prefix.myTest.test')).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT allow you to extend the schema if something else is there', function () {
|
||||
const newSchema = Joi.object({ test: Joi.boolean().default(true) }).default();
|
||||
const run = function () {
|
||||
config.extendSchema('test', newSchema);
|
||||
};
|
||||
expect(run).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeSchema(key)', function () {
|
||||
it('should completely remove the key', function () {
|
||||
const config = new Config(
|
||||
Joi.object().keys({
|
||||
a: Joi.number().default(1),
|
||||
})
|
||||
);
|
||||
|
||||
expect(config.get('a')).toBe(1);
|
||||
config.removeSchema('a');
|
||||
expect(() => config.get('a')).toThrowError('Unknown config key');
|
||||
});
|
||||
|
||||
it('only removes existing keys', function () {
|
||||
const config = new Config(Joi.object());
|
||||
|
||||
expect(() => config.removeSchema('b')).toThrowError('Unknown schema');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { Config } from './config';
|
|
@ -1,119 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { override } from './override';
|
||||
|
||||
describe('override(target, source)', function () {
|
||||
it('should override the values form source to target', function () {
|
||||
const target = {
|
||||
test: {
|
||||
enable: true,
|
||||
host: ['something else'],
|
||||
client: {
|
||||
type: 'sql',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const source = {
|
||||
test: {
|
||||
host: ['host-01', 'host-02'],
|
||||
client: {
|
||||
type: 'nosql',
|
||||
},
|
||||
foo: {
|
||||
bar: {
|
||||
baz: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(override(target, source)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"test": Object {
|
||||
"client": Object {
|
||||
"type": "nosql",
|
||||
},
|
||||
"enable": true,
|
||||
"foo": Object {
|
||||
"bar": Object {
|
||||
"baz": 1,
|
||||
},
|
||||
},
|
||||
"host": Array [
|
||||
"host-01",
|
||||
"host-02",
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('does not mutate arguments', () => {
|
||||
const target = {
|
||||
foo: {
|
||||
bar: 1,
|
||||
baz: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const source = {
|
||||
foo: {
|
||||
bar: 2,
|
||||
},
|
||||
box: 2,
|
||||
};
|
||||
|
||||
expect(override(target, source)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"box": 2,
|
||||
"foo": Object {
|
||||
"bar": 2,
|
||||
"baz": 1,
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(target).not.toHaveProperty('box');
|
||||
expect(source.foo).not.toHaveProperty('baz');
|
||||
});
|
||||
|
||||
it('explodes keys with dots in them', () => {
|
||||
const target = {
|
||||
foo: {
|
||||
bar: 1,
|
||||
},
|
||||
'baz.box.boot.bar.bar': 20,
|
||||
};
|
||||
|
||||
const source = {
|
||||
'foo.bar': 2,
|
||||
'baz.box.boot': {
|
||||
'bar.foo': 10,
|
||||
},
|
||||
};
|
||||
|
||||
expect(override(target, source)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"baz": Object {
|
||||
"box": Object {
|
||||
"boot": Object {
|
||||
"bar": Object {
|
||||
"bar": 20,
|
||||
"foo": 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"foo": Object {
|
||||
"bar": 2,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
const isObject = (v: any): v is Record<string, any> =>
|
||||
typeof v === 'object' && v !== null && !Array.isArray(v);
|
||||
|
||||
const assignDeep = (target: Record<string, any>, source: Record<string, any>) => {
|
||||
for (let [key, value] of Object.entries(source)) {
|
||||
// unwrap dot-separated keys
|
||||
if (key.includes('.')) {
|
||||
const [first, ...others] = key.split('.');
|
||||
key = first;
|
||||
value = { [others.join('.')]: value };
|
||||
}
|
||||
|
||||
if (isObject(value)) {
|
||||
if (!target.hasOwnProperty(key)) {
|
||||
target[key] = {};
|
||||
}
|
||||
|
||||
assignDeep(target[key], value);
|
||||
} else {
|
||||
target[key] = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const override = (...sources: Array<Record<string, any>>): Record<string, any> => {
|
||||
const result = {};
|
||||
|
||||
for (const object of sources) {
|
||||
assignDeep(result, object);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import Joi from 'joi';
|
||||
import os from 'os';
|
||||
import { legacyLoggingConfigSchema } from '@kbn/legacy-logging';
|
||||
|
||||
const HANDLED_IN_NEW_PLATFORM = Joi.any().description(
|
||||
'This key is handled in the new platform ONLY'
|
||||
);
|
||||
export default () =>
|
||||
Joi.object({
|
||||
elastic: Joi.object({
|
||||
apm: HANDLED_IN_NEW_PLATFORM,
|
||||
}).default(),
|
||||
|
||||
pkg: Joi.object({
|
||||
version: Joi.string().default(Joi.ref('$version')),
|
||||
branch: Joi.string().default(Joi.ref('$branch')),
|
||||
buildNum: Joi.number().default(Joi.ref('$buildNum')),
|
||||
buildSha: Joi.string().default(Joi.ref('$buildSha')),
|
||||
}).default(),
|
||||
|
||||
env: Joi.object({
|
||||
name: Joi.string().default(Joi.ref('$env')),
|
||||
dev: Joi.boolean().default(Joi.ref('$dev')),
|
||||
prod: Joi.boolean().default(Joi.ref('$prod')),
|
||||
}).default(),
|
||||
|
||||
dev: HANDLED_IN_NEW_PLATFORM,
|
||||
pid: HANDLED_IN_NEW_PLATFORM,
|
||||
csp: HANDLED_IN_NEW_PLATFORM,
|
||||
|
||||
server: Joi.object({
|
||||
name: Joi.string().default(os.hostname()),
|
||||
// keep them for BWC, remove when not used in Legacy.
|
||||
// validation should be in sync with one in New platform.
|
||||
// https://github.com/elastic/kibana/blob/master/src/core/server/http/http_config.ts
|
||||
basePath: Joi.string()
|
||||
.default('')
|
||||
.allow('')
|
||||
.regex(/(^$|^\/.*[^\/]$)/, `start with a slash, don't end with one`),
|
||||
host: Joi.string().hostname().default('localhost'),
|
||||
port: Joi.number().default(5601),
|
||||
rewriteBasePath: Joi.boolean().when('basePath', {
|
||||
is: '',
|
||||
then: Joi.default(false).valid(false),
|
||||
otherwise: Joi.default(false),
|
||||
}),
|
||||
|
||||
autoListen: HANDLED_IN_NEW_PLATFORM,
|
||||
cors: HANDLED_IN_NEW_PLATFORM,
|
||||
customResponseHeaders: HANDLED_IN_NEW_PLATFORM,
|
||||
keepaliveTimeout: HANDLED_IN_NEW_PLATFORM,
|
||||
maxPayloadBytes: HANDLED_IN_NEW_PLATFORM,
|
||||
publicBaseUrl: HANDLED_IN_NEW_PLATFORM,
|
||||
socketTimeout: HANDLED_IN_NEW_PLATFORM,
|
||||
ssl: HANDLED_IN_NEW_PLATFORM,
|
||||
compression: HANDLED_IN_NEW_PLATFORM,
|
||||
uuid: HANDLED_IN_NEW_PLATFORM,
|
||||
xsrf: HANDLED_IN_NEW_PLATFORM,
|
||||
}).default(),
|
||||
|
||||
uiSettings: HANDLED_IN_NEW_PLATFORM,
|
||||
|
||||
logging: legacyLoggingConfigSchema,
|
||||
|
||||
ops: Joi.object({
|
||||
interval: Joi.number().default(5000),
|
||||
cGroupOverrides: HANDLED_IN_NEW_PLATFORM,
|
||||
}).default(),
|
||||
|
||||
plugins: HANDLED_IN_NEW_PLATFORM,
|
||||
path: HANDLED_IN_NEW_PLATFORM,
|
||||
stats: HANDLED_IN_NEW_PLATFORM,
|
||||
status: HANDLED_IN_NEW_PLATFORM,
|
||||
map: HANDLED_IN_NEW_PLATFORM,
|
||||
i18n: HANDLED_IN_NEW_PLATFORM,
|
||||
|
||||
// temporarily moved here from the (now deleted) kibana legacy plugin
|
||||
kibana: Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
index: Joi.string().default('.kibana'),
|
||||
autocompleteTerminateAfter: Joi.number().integer().min(1).default(100000),
|
||||
// TODO Also allow units here like in elasticsearch config once this is moved to the new platform
|
||||
autocompleteTimeout: Joi.number().integer().min(1).default(1000),
|
||||
}).default(),
|
||||
|
||||
savedObjects: HANDLED_IN_NEW_PLATFORM,
|
||||
}).default();
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import schemaProvider from './schema';
|
||||
import Joi from 'joi';
|
||||
|
||||
describe('Config schema', function () {
|
||||
let schema;
|
||||
beforeEach(async () => (schema = await schemaProvider()));
|
||||
|
||||
function validate(data, options) {
|
||||
return Joi.validate(data, schema, options);
|
||||
}
|
||||
|
||||
describe('server', function () {
|
||||
it('everything is optional', function () {
|
||||
const { error } = validate({});
|
||||
expect(error).toBe(null);
|
||||
});
|
||||
|
||||
describe('basePath', function () {
|
||||
it('accepts empty strings', function () {
|
||||
const { error, value } = validate({ server: { basePath: '' } });
|
||||
expect(error).toBe(null);
|
||||
expect(value.server.basePath).toBe('');
|
||||
});
|
||||
|
||||
it('accepts strings with leading slashes', function () {
|
||||
const { error, value } = validate({ server: { basePath: '/path' } });
|
||||
expect(error).toBe(null);
|
||||
expect(value.server.basePath).toBe('/path');
|
||||
});
|
||||
|
||||
it('rejects strings with trailing slashes', function () {
|
||||
const { error } = validate({ server: { basePath: '/path/' } });
|
||||
expect(error).toHaveProperty('details');
|
||||
expect(error.details[0]).toHaveProperty('path', ['server', 'basePath']);
|
||||
});
|
||||
|
||||
it('rejects strings without leading slashes', function () {
|
||||
const { error } = validate({ server: { basePath: 'path' } });
|
||||
expect(error).toHaveProperty('details');
|
||||
expect(error.details[0]).toHaveProperty('path', ['server', 'basePath']);
|
||||
});
|
||||
|
||||
it('rejects things that are not strings', function () {
|
||||
for (const value of [1, true, {}, [], /foo/]) {
|
||||
const { error } = validate({ server: { basePath: value } });
|
||||
expect(error).toHaveProperty('details');
|
||||
expect(error.details[0]).toHaveProperty('path', ['server', 'basePath']);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('rewriteBasePath', function () {
|
||||
it('defaults to false', () => {
|
||||
const { error, value } = validate({});
|
||||
expect(error).toBe(null);
|
||||
expect(value.server.rewriteBasePath).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts false', function () {
|
||||
const { error, value } = validate({ server: { rewriteBasePath: false } });
|
||||
expect(error).toBe(null);
|
||||
expect(value.server.rewriteBasePath).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts true if basePath set', function () {
|
||||
const { error, value } = validate({ server: { basePath: '/foo', rewriteBasePath: true } });
|
||||
expect(error).toBe(null);
|
||||
expect(value.server.rewriteBasePath).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects true if basePath not set', function () {
|
||||
const { error } = validate({ server: { rewriteBasePath: true } });
|
||||
expect(error).toHaveProperty('details');
|
||||
expect(error.details[0]).toHaveProperty('path', ['server', 'rewriteBasePath']);
|
||||
});
|
||||
|
||||
it('rejects strings', function () {
|
||||
const { error } = validate({ server: { rewriteBasePath: 'foo' } });
|
||||
expect(error).toHaveProperty('details');
|
||||
expect(error.details[0]).toHaveProperty('path', ['server', 'rewriteBasePath']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Server } from '@hapi/hapi';
|
||||
import KbnServer from '../kbn_server';
|
||||
|
||||
/**
|
||||
* Exposes `kbnServer.newPlatform` through Hapi API.
|
||||
* @param kbnServer KbnServer singleton instance.
|
||||
* @param server Hapi server instance to expose `core` on.
|
||||
*/
|
||||
export function coreMixin(kbnServer: KbnServer, server: Server) {
|
||||
// we suppress type error because hapi expect a function here not an object
|
||||
server.decorate('server', 'newPlatform', kbnServer.newPlatform as any);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { format } from 'url';
|
||||
import Boom from '@hapi/boom';
|
||||
|
||||
export default async function (kbnServer, server) {
|
||||
server = kbnServer.server;
|
||||
|
||||
const getBasePath = (request) => kbnServer.newPlatform.setup.core.http.basePath.get(request);
|
||||
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: '/{p*}',
|
||||
handler: function (req, h) {
|
||||
const path = req.path;
|
||||
if (path === '/' || path.charAt(path.length - 1) !== '/') {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
const basePath = getBasePath(req);
|
||||
const pathPrefix = basePath ? `${basePath}/` : '';
|
||||
return h
|
||||
.redirect(
|
||||
format({
|
||||
search: req.url.search,
|
||||
pathname: pathPrefix + path.slice(0, -1),
|
||||
})
|
||||
)
|
||||
.permanent(true);
|
||||
},
|
||||
});
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/src/legacy/server'],
|
||||
};
|
95
src/legacy/server/kbn_server.d.ts
vendored
95
src/legacy/server/kbn_server.d.ts
vendored
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { Server } from '@hapi/hapi';
|
||||
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
EnvironmentMode,
|
||||
LoggerFactory,
|
||||
PackageInfo,
|
||||
LegacyServiceSetupDeps,
|
||||
} from '../../core/server';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { LegacyConfig } from '../../core/server/legacy';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { UiPlugins } from '../../core/server/plugins';
|
||||
|
||||
// lot of legacy code was assuming this type only had these two methods
|
||||
export type KibanaConfig = Pick<LegacyConfig, 'get' | 'has'>;
|
||||
|
||||
// Extend the defaults with the plugins and server methods we need.
|
||||
declare module 'hapi' {
|
||||
interface PluginProperties {
|
||||
spaces: any;
|
||||
}
|
||||
|
||||
interface Server {
|
||||
config: () => KibanaConfig;
|
||||
newPlatform: KbnServer['newPlatform'];
|
||||
}
|
||||
}
|
||||
|
||||
type KbnMixinFunc = (kbnServer: KbnServer, server: Server, config: any) => Promise<any> | void;
|
||||
|
||||
export interface PluginsSetup {
|
||||
[key: string]: object;
|
||||
}
|
||||
|
||||
export interface KibanaCore {
|
||||
__internals: {
|
||||
hapiServer: LegacyServiceSetupDeps['core']['http']['server'];
|
||||
rendering: LegacyServiceSetupDeps['core']['rendering'];
|
||||
uiPlugins: UiPlugins;
|
||||
};
|
||||
env: {
|
||||
mode: Readonly<EnvironmentMode>;
|
||||
packageInfo: Readonly<PackageInfo>;
|
||||
};
|
||||
setupDeps: {
|
||||
core: CoreSetup;
|
||||
plugins: PluginsSetup;
|
||||
};
|
||||
startDeps: {
|
||||
core: CoreStart;
|
||||
plugins: Record<string, object>;
|
||||
};
|
||||
logger: LoggerFactory;
|
||||
}
|
||||
|
||||
export interface NewPlatform {
|
||||
__internals: KibanaCore['__internals'];
|
||||
env: KibanaCore['env'];
|
||||
coreContext: {
|
||||
logger: KibanaCore['logger'];
|
||||
};
|
||||
setup: KibanaCore['setupDeps'];
|
||||
start: KibanaCore['startDeps'];
|
||||
stop: null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default class KbnServer {
|
||||
public readonly newPlatform: NewPlatform;
|
||||
public server: Server;
|
||||
public inject: Server['inject'];
|
||||
|
||||
constructor(settings: Record<string, any>, config: KibanaConfig, core: KibanaCore);
|
||||
|
||||
public ready(): Promise<void>;
|
||||
public mixin(...fns: KbnMixinFunc[]): Promise<void>;
|
||||
public listen(): Promise<Server>;
|
||||
public close(): Promise<void>;
|
||||
public applyLoggingConfiguration(settings: any): void;
|
||||
public config: KibanaConfig;
|
||||
}
|
||||
|
||||
// Re-export commonly used hapi types.
|
||||
export { Server, Request, ResponseToolkit } from '@hapi/hapi';
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { constant, once, compact, flatten } from 'lodash';
|
||||
import { reconfigureLogging } from '@kbn/legacy-logging';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { fromRoot, pkg } from '../../core/server/utils';
|
||||
import { Config } from './config';
|
||||
import httpMixin from './http';
|
||||
import { coreMixin } from './core';
|
||||
import { loggingMixin } from './logging';
|
||||
|
||||
/**
|
||||
* @typedef {import('./kbn_server').KibanaConfig} KibanaConfig
|
||||
* @typedef {import('./kbn_server').KibanaCore} KibanaCore
|
||||
* @typedef {import('./kbn_server').LegacyPlugins} LegacyPlugins
|
||||
*/
|
||||
|
||||
const rootDir = fromRoot('.');
|
||||
|
||||
export default class KbnServer {
|
||||
/**
|
||||
* @param {Record<string, any>} settings
|
||||
* @param {KibanaConfig} config
|
||||
* @param {KibanaCore} core
|
||||
*/
|
||||
constructor(settings, config, core) {
|
||||
this.name = pkg.name;
|
||||
this.version = pkg.version;
|
||||
this.build = pkg.build || false;
|
||||
this.rootDir = rootDir;
|
||||
this.settings = settings || {};
|
||||
this.config = config;
|
||||
|
||||
const { setupDeps, startDeps, logger, __internals, env } = core;
|
||||
|
||||
this.server = __internals.hapiServer;
|
||||
this.newPlatform = {
|
||||
env: {
|
||||
mode: env.mode,
|
||||
packageInfo: env.packageInfo,
|
||||
},
|
||||
__internals,
|
||||
coreContext: {
|
||||
logger,
|
||||
},
|
||||
setup: setupDeps,
|
||||
start: startDeps,
|
||||
stop: null,
|
||||
};
|
||||
|
||||
this.ready = constant(
|
||||
this.mixin(
|
||||
// Sets global HTTP behaviors
|
||||
httpMixin,
|
||||
|
||||
coreMixin,
|
||||
|
||||
loggingMixin
|
||||
)
|
||||
);
|
||||
|
||||
this.listen = once(this.listen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the KbnServer outside of the constraints of a plugin. This allows access
|
||||
* to APIs that are not exposed (intentionally) to the plugins and should only
|
||||
* be used when the code will be kept up to date with Kibana.
|
||||
*
|
||||
* @param {...function} - functions that should be called to mixin functionality.
|
||||
* They are called with the arguments (kibana, server, config)
|
||||
* and can return a promise to delay execution of the next mixin
|
||||
* @return {Promise} - promise that is resolved when the final mixin completes.
|
||||
*/
|
||||
async mixin(...fns) {
|
||||
for (const fn of compact(flatten(fns))) {
|
||||
await fn.call(this, this, this.server, this.config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the server to listen for incoming requests, or get
|
||||
* a promise that will be resolved once the server is listening.
|
||||
*
|
||||
* @return undefined
|
||||
*/
|
||||
async listen() {
|
||||
await this.ready();
|
||||
|
||||
const { server } = this;
|
||||
|
||||
if (process.env.isDevCliChild) {
|
||||
// help parent process know when we are ready
|
||||
process.send(['SERVER_LISTENING']);
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (!this.server) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.server.stop();
|
||||
}
|
||||
|
||||
async inject(opts) {
|
||||
if (!this.server) {
|
||||
await this.ready();
|
||||
}
|
||||
|
||||
return await this.server.inject(opts);
|
||||
}
|
||||
|
||||
applyLoggingConfiguration(settings) {
|
||||
const config = Config.withDefaultSchema(settings);
|
||||
|
||||
const loggingConfig = config.get('logging');
|
||||
const opsConfig = config.get('ops');
|
||||
|
||||
reconfigureLogging(this.server, loggingConfig, opsConfig.interval);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { setupLogging, setupLoggingRotate } from '@kbn/legacy-logging';
|
||||
|
||||
export async function loggingMixin(kbnServer, server, config) {
|
||||
const loggingConfig = config.get('logging');
|
||||
const opsInterval = config.get('ops.interval');
|
||||
|
||||
await setupLogging(server, loggingConfig, opsInterval);
|
||||
await setupLoggingRotate(server, loggingConfig);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { pkg } from '../../core/server/utils';
|
||||
export const IS_KIBANA_DISTRIBUTABLE = pkg.build && pkg.build.distributable === true;
|
||||
export const IS_KIBANA_RELEASE = pkg.build && pkg.build.release === true;
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { deepCloneWithBuffers } from './deep_clone_with_buffers';
|
||||
|
||||
describe('deepCloneWithBuffers()', () => {
|
||||
it('deep clones objects', () => {
|
||||
const source = {
|
||||
a: {
|
||||
b: {},
|
||||
c: {},
|
||||
d: [
|
||||
{
|
||||
e: 'f',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const output = deepCloneWithBuffers(source);
|
||||
|
||||
expect(source.a).toEqual(output.a);
|
||||
expect(source.a).not.toBe(output.a);
|
||||
|
||||
expect(source.a.b).toEqual(output.a.b);
|
||||
expect(source.a.b).not.toBe(output.a.b);
|
||||
|
||||
expect(source.a.c).toEqual(output.a.c);
|
||||
expect(source.a.c).not.toBe(output.a.c);
|
||||
|
||||
expect(source.a.d).toEqual(output.a.d);
|
||||
expect(source.a.d).not.toBe(output.a.d);
|
||||
|
||||
expect(source.a.d[0]).toEqual(output.a.d[0]);
|
||||
expect(source.a.d[0]).not.toBe(output.a.d[0]);
|
||||
});
|
||||
|
||||
it('copies buffers but keeps them buffers', () => {
|
||||
const input = Buffer.from('i am a teapot', 'utf8');
|
||||
const output = deepCloneWithBuffers(input);
|
||||
|
||||
expect(Buffer.isBuffer(input)).toBe(true);
|
||||
expect(Buffer.isBuffer(output)).toBe(true);
|
||||
expect(Buffer.compare(output, input));
|
||||
expect(output).not.toBe(input);
|
||||
});
|
||||
|
||||
it('copies buffers that are deep', () => {
|
||||
const input = {
|
||||
a: {
|
||||
b: {
|
||||
c: Buffer.from('i am a teapot', 'utf8'),
|
||||
},
|
||||
},
|
||||
};
|
||||
const output = deepCloneWithBuffers(input);
|
||||
|
||||
expect(Buffer.isBuffer(input.a.b.c)).toBe(true);
|
||||
expect(Buffer.isBuffer(output.a.b.c)).toBe(true);
|
||||
expect(Buffer.compare(output.a.b.c, input.a.b.c));
|
||||
expect(output.a.b.c).not.toBe(input.a.b.c);
|
||||
});
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { cloneDeepWith } from 'lodash';
|
||||
|
||||
// We should add `any` return type to overcome bug in lodash types, customizer
|
||||
// in lodash 3.* can return `undefined` if cloning is handled by the lodash, but
|
||||
// type of the customizer function doesn't expect that.
|
||||
function cloneBuffersCustomizer(val: unknown): any {
|
||||
if (Buffer.isBuffer(val)) {
|
||||
return Buffer.from(val);
|
||||
}
|
||||
}
|
||||
|
||||
export function deepCloneWithBuffers<T>(val: T): T {
|
||||
return cloneDeepWith(val, cloneBuffersCustomizer);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue