mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -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
This commit is contained in:
parent
d05d5634b3
commit
251bd9afc6
111 changed files with 745 additions and 4106 deletions
|
@ -21,19 +21,13 @@ snapshots.js
|
||||||
|
|
||||||
# plugin overrides
|
# plugin overrides
|
||||||
/src/core/lib/kbn_internal_native_observable
|
/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/data/common/es_query/kuery/ast/_generated_/**
|
||||||
/src/plugins/vis_type_timelion/common/_generated_/**
|
/src/plugins/vis_type_timelion/common/_generated_/**
|
||||||
/x-pack/legacy/plugins/**/__tests__/fixtures/**
|
|
||||||
/x-pack/plugins/apm/e2e/tmp/*
|
/x-pack/plugins/apm/e2e/tmp/*
|
||||||
/x-pack/plugins/canvas/canvas_plugin
|
/x-pack/plugins/canvas/canvas_plugin
|
||||||
/x-pack/plugins/canvas/shareable_runtime/build
|
/x-pack/plugins/canvas/shareable_runtime/build
|
||||||
/x-pack/plugins/canvas/storybook/build
|
/x-pack/plugins/canvas/storybook/build
|
||||||
/x-pack/plugins/reporting/server/export_types/printable_pdf/server/lib/pdf/assets/**
|
/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
|
# package overrides
|
||||||
/packages/elastic-eslint-config-kibana
|
/packages/elastic-eslint-config-kibana
|
||||||
|
|
24
.eslintrc.js
24
.eslintrc.js
|
@ -410,11 +410,7 @@ module.exports = {
|
||||||
errorMessage: `Common code can not import from server or public, use a common directory.`,
|
errorMessage: `Common code can not import from server or public, use a common directory.`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: [
|
target: ['(src|x-pack)/plugins/**/(public|server)/**/*', 'examples/**/*'],
|
||||||
'src/legacy/**/*',
|
|
||||||
'(src|x-pack)/plugins/**/(public|server)/**/*',
|
|
||||||
'examples/**/*',
|
|
||||||
],
|
|
||||||
from: [
|
from: [
|
||||||
'src/core/public/**/*',
|
'src/core/public/**/*',
|
||||||
'!src/core/public/index.ts', // relative import
|
'!src/core/public/index.ts', // relative import
|
||||||
|
@ -428,8 +424,6 @@ module.exports = {
|
||||||
'!src/core/server/mocks{,.ts}',
|
'!src/core/server/mocks{,.ts}',
|
||||||
'!src/core/server/types{,.ts}',
|
'!src/core/server/types{,.ts}',
|
||||||
'!src/core/server/test_utils{,.ts}',
|
'!src/core/server/test_utils{,.ts}',
|
||||||
'!src/core/server/utils', // ts alias
|
|
||||||
'!src/core/server/utils/**/*',
|
|
||||||
// for absolute imports until fixed in
|
// for absolute imports until fixed in
|
||||||
// https://github.com/elastic/kibana/issues/36096
|
// https://github.com/elastic/kibana/issues/36096
|
||||||
'!src/core/server/*.test.mocks{,.ts}',
|
'!src/core/server/*.test.mocks{,.ts}',
|
||||||
|
@ -442,7 +436,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: [
|
target: [
|
||||||
'src/legacy/**/*',
|
|
||||||
'(src|x-pack)/plugins/**/(public|server)/**/*',
|
'(src|x-pack)/plugins/**/(public|server)/**/*',
|
||||||
'examples/**/*',
|
'examples/**/*',
|
||||||
'!(src|x-pack)/**/*.test.*',
|
'!(src|x-pack)/**/*.test.*',
|
||||||
|
@ -482,7 +475,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
target: ['src/core/**/*'],
|
target: ['src/core/**/*'],
|
||||||
from: ['plugins/**/*', 'src/plugins/**/*', 'src/legacy/ui/**/*'],
|
from: ['plugins/**/*', 'src/plugins/**/*'],
|
||||||
errorMessage: 'The core cannot depend on any plugins.',
|
errorMessage: 'The core cannot depend on any plugins.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -490,19 +483,6 @@ module.exports = {
|
||||||
from: ['ui/**/*'],
|
from: ['ui/**/*'],
|
||||||
errorMessage: 'Plugins cannot import legacy UI code.',
|
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.',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
8
.github/CODEOWNERS
vendored
8
.github/CODEOWNERS
vendored
|
@ -107,7 +107,6 @@
|
||||||
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation
|
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation
|
||||||
/x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation
|
/x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation
|
||||||
#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation
|
#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation
|
||||||
#CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-presentation
|
|
||||||
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation
|
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,7 +163,6 @@
|
||||||
/packages/kbn-utils/ @elastic/kibana-operations
|
/packages/kbn-utils/ @elastic/kibana-operations
|
||||||
/packages/kbn-cli-dev-mode/ @elastic/kibana-operations
|
/packages/kbn-cli-dev-mode/ @elastic/kibana-operations
|
||||||
/src/cli/keystore/ @elastic/kibana-operations
|
/src/cli/keystore/ @elastic/kibana-operations
|
||||||
/src/legacy/server/warnings/ @elastic/kibana-operations
|
|
||||||
/.ci/es-snapshots/ @elastic/kibana-operations
|
/.ci/es-snapshots/ @elastic/kibana-operations
|
||||||
/.github/workflows/ @elastic/kibana-operations
|
/.github/workflows/ @elastic/kibana-operations
|
||||||
/vars/ @elastic/kibana-operations
|
/vars/ @elastic/kibana-operations
|
||||||
|
@ -201,9 +199,6 @@
|
||||||
/packages/kbn-legacy-logging/ @elastic/kibana-core
|
/packages/kbn-legacy-logging/ @elastic/kibana-core
|
||||||
/packages/kbn-crypto/ @elastic/kibana-core
|
/packages/kbn-crypto/ @elastic/kibana-core
|
||||||
/packages/kbn-http-tools/ @elastic/kibana-core
|
/packages/kbn-http-tools/ @elastic/kibana-core
|
||||||
/src/legacy/server/config/ @elastic/kibana-core
|
|
||||||
/src/legacy/server/http/ @elastic/kibana-core
|
|
||||||
/src/legacy/server/logging/ @elastic/kibana-core
|
|
||||||
/src/plugins/status_page/ @elastic/kibana-core
|
/src/plugins/status_page/ @elastic/kibana-core
|
||||||
/src/plugins/saved_objects_management/ @elastic/kibana-core
|
/src/plugins/saved_objects_management/ @elastic/kibana-core
|
||||||
/src/dev/run_check_published_api_changes.ts @elastic/kibana-core
|
/src/dev/run_check_published_api_changes.ts @elastic/kibana-core
|
||||||
|
@ -213,9 +208,6 @@
|
||||||
/src/plugins/kibana_overview/ @elastic/kibana-core
|
/src/plugins/kibana_overview/ @elastic/kibana-core
|
||||||
/x-pack/plugins/global_search_bar/ @elastic/kibana-core
|
/x-pack/plugins/global_search_bar/ @elastic/kibana-core
|
||||||
#CC# /src/core/server/csp/ @elastic/kibana-core
|
#CC# /src/core/server/csp/ @elastic/kibana-core
|
||||||
#CC# /src/legacy/server/config/ @elastic/kibana-core
|
|
||||||
#CC# /src/legacy/server/http/ @elastic/kibana-core
|
|
||||||
#CC# /src/legacy/ui/public/documentation_links @elastic/kibana-core
|
|
||||||
#CC# /src/plugins/legacy_export/ @elastic/kibana-core
|
#CC# /src/plugins/legacy_export/ @elastic/kibana-core
|
||||||
#CC# /src/plugins/xpack_legacy/ @elastic/kibana-core
|
#CC# /src/plugins/xpack_legacy/ @elastic/kibana-core
|
||||||
#CC# /src/plugins/saved_objects/ @elastic/kibana-core
|
#CC# /src/plugins/saved_objects/ @elastic/kibana-core
|
||||||
|
|
|
@ -10,10 +10,10 @@ Set of helpers used to create `KibanaResponse` to form HTTP response on an incom
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
kibanaResponseFactory: {
|
kibanaResponseFactory: {
|
||||||
custom: <T extends string | Record<string, any> | Error | Buffer | {
|
custom: <T extends string | Record<string, any> | Error | Buffer | Stream | {
|
||||||
message: string | Error;
|
message: string | Error;
|
||||||
attributes?: Record<string, any> | undefined;
|
attributes?: Record<string, any> | undefined;
|
||||||
} | Stream | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
} | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||||
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||||
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||||
forbidden: (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. |
|
| [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. |
|
| [LegacyElasticsearchError](./kibana-plugin-core-server.legacyelasticsearcherror.md) | @<!-- -->deprecated. The new elasticsearch client doesn't wrap errors anymore. |
|
||||||
| [LegacyRequest](./kibana-plugin-core-server.legacyrequest.md) | |
|
| [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) | |
|
| [LoggerContextConfigInput](./kibana-plugin-core-server.loggercontextconfiginput.md) | |
|
||||||
| [LoggingServiceSetup](./kibana-plugin-core-server.loggingservicesetup.md) | Provides APIs to plugins for customizing the plugin's logger. |
|
| [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. |
|
| [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>;
|
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
|
||||||
};
|
};
|
||||||
indexPatterns: {
|
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>>;
|
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>;
|
fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise<import("../common").FieldFormatsRegistry>;
|
||||||
};
|
};
|
||||||
indexPatterns: {
|
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>>;
|
search: ISearchStart<import("./search").IEsSearchRequest, import("./search").IEsSearchResponse<any>>;
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -12,7 +12,6 @@ module.exports = {
|
||||||
projects: [
|
projects: [
|
||||||
'<rootDir>/packages/*/jest.config.js',
|
'<rootDir>/packages/*/jest.config.js',
|
||||||
'<rootDir>/src/*/jest.config.js',
|
'<rootDir>/src/*/jest.config.js',
|
||||||
'<rootDir>/src/legacy/*/jest.config.js',
|
|
||||||
'<rootDir>/src/plugins/*/jest.config.js',
|
'<rootDir>/src/plugins/*/jest.config.js',
|
||||||
'<rootDir>/test/*/jest.config.js',
|
'<rootDir>/test/*/jest.config.js',
|
||||||
'<rootDir>/x-pack/plugins/*/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';
|
import * as Server from 'src/core/server';
|
||||||
|
|
||||||
export { Public, 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(`
|
expect(watchPaths).toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
<absolute path>/src/core,
|
<absolute path>/src/core,
|
||||||
<absolute path>/src/legacy/server,
|
|
||||||
<absolute path>/src/legacy/utils,
|
|
||||||
<absolute path>/config,
|
<absolute path>/config,
|
||||||
<absolute path>/x-pack/test/plugin_functional/plugins/resolver_test,
|
<absolute path>/x-pack/test/plugin_functional/plugins/resolver_test,
|
||||||
<absolute path>/src/plugins,
|
<absolute path>/src/plugins,
|
||||||
|
|
|
@ -1,69 +1,5 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// 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`] = `
|
exports[`#get correctly handles silent logging config. 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"appenders": Object {
|
"appenders": Object {
|
||||||
|
@ -78,6 +14,7 @@ Object {
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"level": "off",
|
"level": "off",
|
||||||
},
|
},
|
||||||
|
"silent": true,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -93,10 +30,13 @@ Object {
|
||||||
"type": "legacy-appender",
|
"type": "legacy-appender",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"dest": "/some/path.log",
|
||||||
|
"json": true,
|
||||||
"loggers": undefined,
|
"loggers": undefined,
|
||||||
"root": Object {
|
"root": Object {
|
||||||
"level": "all",
|
"level": "all",
|
||||||
},
|
},
|
||||||
|
"verbose": true,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
@ -65,59 +65,6 @@ describe('#get', () => {
|
||||||
|
|
||||||
expect(configAdapter.get('logging')).toMatchSnapshot();
|
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', () => {
|
describe('#set', () => {
|
||||||
|
|
|
@ -9,15 +9,6 @@
|
||||||
import { ConfigPath } from '../config';
|
import { ConfigPath } from '../config';
|
||||||
import { ObjectToConfigAdapter } from '../object_to_config_adapter';
|
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.
|
* Represents logging config supported by the legacy platform.
|
||||||
*/
|
*/
|
||||||
|
@ -30,7 +21,7 @@ export interface LegacyLoggingConfig {
|
||||||
events?: Record<string, string>;
|
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`
|
* Represents adapter between config provided by legacy platform and `Config`
|
||||||
|
@ -48,6 +39,7 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter {
|
||||||
},
|
},
|
||||||
root: { level: 'info', ...root },
|
root: { level: 'info', ...root },
|
||||||
loggers,
|
loggers,
|
||||||
|
...legacyLoggingConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (configValue.silent) {
|
if (configValue.silent) {
|
||||||
|
@ -61,47 +53,11 @@ export class LegacyObjectToConfigAdapter extends ObjectToConfigAdapter {
|
||||||
return loggingConfig;
|
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) {
|
public get(configPath: ConfigPath) {
|
||||||
const configValue = super.get(configPath);
|
const configValue = super.get(configPath);
|
||||||
switch (configPath) {
|
switch (configPath) {
|
||||||
case 'logging':
|
case 'logging':
|
||||||
return LegacyObjectToConfigAdapter.transformLogging(configValue as LegacyLoggingConfig);
|
return LegacyObjectToConfigAdapter.transformLogging(configValue as LegacyLoggingConfig);
|
||||||
case 'server':
|
|
||||||
return LegacyObjectToConfigAdapter.transformServer(configValue);
|
|
||||||
case 'plugins':
|
|
||||||
return LegacyObjectToConfigAdapter.transformPlugins(configValue as LegacyVars);
|
|
||||||
default:
|
default:
|
||||||
return configValue;
|
return configValue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"kbn:watch": "yarn build --watch"
|
"kbn:watch": "yarn build --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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
|
// 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
|
// that doesn't exist to avoid logging of ops at all, if turned on it will be
|
||||||
// logged by the "legacy" Kibana.
|
// logged by the "legacy" Kibana.
|
||||||
const { value: loggingConfig } = legacyLoggingConfigSchema.validate({
|
const loggingConfig = legacyLoggingConfigSchema.validate({
|
||||||
...legacyLoggingConfig,
|
...legacyLoggingConfig,
|
||||||
events: {
|
events: {
|
||||||
...legacyLoggingConfig.events,
|
...legacyLoggingConfig.events,
|
||||||
|
|
|
@ -6,11 +6,8 @@
|
||||||
* Side Public License, v 1.
|
* 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
|
* @deprecated
|
||||||
*
|
*
|
||||||
|
@ -36,46 +33,65 @@ export interface LegacyLoggingConfig {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const legacyLoggingConfigSchema = Joi.object()
|
export const legacyLoggingConfigSchema = schema.object({
|
||||||
.keys({
|
silent: schema.boolean({ defaultValue: false }),
|
||||||
appenders: HANDLED_IN_KIBANA_PLATFORM,
|
quiet: schema.conditional(
|
||||||
loggers: HANDLED_IN_KIBANA_PLATFORM,
|
schema.siblingRef('silent'),
|
||||||
root: HANDLED_IN_KIBANA_PLATFORM,
|
true,
|
||||||
|
schema.boolean({
|
||||||
silent: Joi.boolean().default(false),
|
defaultValue: true,
|
||||||
quiet: Joi.boolean().when('silent', {
|
validate: (quiet) => {
|
||||||
is: true,
|
if (!quiet) {
|
||||||
then: Joi.boolean().default(true).valid(true),
|
return 'must be true when `silent` is true';
|
||||||
otherwise: Joi.boolean().default(false),
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
verbose: Joi.boolean().when('quiet', {
|
schema.boolean({ defaultValue: false })
|
||||||
is: true,
|
),
|
||||||
then: Joi.valid(false).default(false),
|
verbose: schema.conditional(
|
||||||
otherwise: Joi.boolean().default(false),
|
schema.siblingRef('quiet'),
|
||||||
|
true,
|
||||||
|
schema.boolean({
|
||||||
|
defaultValue: false,
|
||||||
|
validate: (verbose) => {
|
||||||
|
if (verbose) {
|
||||||
|
return 'must be false when `quiet` is true';
|
||||||
|
}
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
events: Joi.any().default({}),
|
schema.boolean({ defaultValue: false })
|
||||||
dest: Joi.string().default('stdout'),
|
),
|
||||||
filter: Joi.any().default({}),
|
events: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
|
||||||
json: Joi.boolean().when('dest', {
|
dest: schema.string({ defaultValue: 'stdout' }),
|
||||||
is: 'stdout',
|
filter: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }),
|
||||||
then: Joi.boolean().default(!process.stdout.isTTY),
|
json: schema.conditional(
|
||||||
otherwise: Joi.boolean().default(true),
|
schema.siblingRef('dest'),
|
||||||
|
'stdout',
|
||||||
|
schema.boolean({
|
||||||
|
defaultValue: !process.stdout.isTTY,
|
||||||
}),
|
}),
|
||||||
timezone: Joi.string(),
|
schema.boolean({
|
||||||
rotate: Joi.object()
|
defaultValue: true,
|
||||||
.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(),
|
),
|
||||||
})
|
timezone: schema.maybe(schema.string()),
|
||||||
.default();
|
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')),
|
__dirname: dirname(resolve(REPO_ROOT, 'package.json')),
|
||||||
...require(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 _ from 'lodash';
|
||||||
import { pkg } from '../core/server/utils';
|
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||||
import Command from './command';
|
import Command from './command';
|
||||||
import serveCommand from './serve/serve';
|
import serveCommand from './serve/serve';
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,7 @@ import { statSync } from 'fs';
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
import { getConfigPath, fromRoot } from '@kbn/utils';
|
import { getConfigPath, fromRoot, isKibanaDistributable } from '@kbn/utils';
|
||||||
import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
|
|
||||||
import { readKeystore } from '../keystore/read_keystore';
|
import { readKeystore } from '../keystore/read_keystore';
|
||||||
|
|
||||||
function canRequire(path) {
|
function canRequire(path) {
|
||||||
|
@ -65,9 +64,10 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) {
|
||||||
delete rawConfig.xpack;
|
delete rawConfig.xpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.dev) {
|
// only used to set cliArgs.envName, we don't want to inject that into the config
|
||||||
set('env', 'development');
|
delete extraCliOptions.env;
|
||||||
|
|
||||||
|
if (opts.dev) {
|
||||||
if (!has('elasticsearch.username')) {
|
if (!has('elasticsearch.username')) {
|
||||||
set('elasticsearch.username', 'kibana_system');
|
set('elasticsearch.username', 'kibana_system');
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ export default function (program) {
|
||||||
.option('--plugins <path>', 'an alias for --plugin-dir', pluginDirCollector)
|
.option('--plugins <path>', 'an alias for --plugin-dir', pluginDirCollector)
|
||||||
.option('--optimize', 'Deprecated, running the optimizer is no longer required');
|
.option('--optimize', 'Deprecated, running the optimizer is no longer required');
|
||||||
|
|
||||||
if (!IS_KIBANA_DISTRIBUTABLE) {
|
if (!isKibanaDistributable()) {
|
||||||
command
|
command
|
||||||
.option('--oss', 'Start Kibana without X-Pack')
|
.option('--oss', 'Start Kibana without X-Pack')
|
||||||
.option(
|
.option(
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { pkg } from '../core/server/utils';
|
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||||
|
|
||||||
import Command from '../cli/command';
|
import Command from '../cli/command';
|
||||||
import { EncryptionConfig } from './encryption_config';
|
import { EncryptionConfig } from './encryption_config';
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||||
|
|
||||||
import { pkg } from '../core/server/utils';
|
|
||||||
import Command from '../cli/command';
|
import Command from '../cli/command';
|
||||||
import { Keystore } from '../cli/keystore';
|
import { Keystore } from '../cli/keystore';
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { pkg } from '../core/server/utils';
|
import { kibanaPackageJson as pkg } from '@kbn/utils';
|
||||||
import Command from '../cli/command';
|
import Command from '../cli/command';
|
||||||
import { listCommand } from './list';
|
import { listCommand } from './list';
|
||||||
import { installCommand } from './install';
|
import { installCommand } from './install';
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getConfigPath } from '@kbn/utils';
|
import { getConfigPath, kibanaPackageJson as pkg } from '@kbn/utils';
|
||||||
import { pkg } from '../../core/server/utils';
|
|
||||||
import { install } from './install';
|
import { install } from './install';
|
||||||
import { Logger } from '../lib/logger';
|
import { Logger } from '../lib/logger';
|
||||||
import { parse, parseMilliseconds } from './settings';
|
import { parse, parseMilliseconds } from './settings';
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { statSync } from 'fs';
|
import { statSync } from 'fs';
|
||||||
|
|
||||||
import { versionSatisfies, cleanVersion } from '../../legacy/utils/version';
|
import { versionSatisfies, cleanVersion } from './utils/version';
|
||||||
|
|
||||||
export function existingInstall(settings, logger) {
|
export function existingInstall(settings, logger) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -7,10 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
|
||||||
import expiry from 'expiry-js';
|
import expiry from 'expiry-js';
|
||||||
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { fromRoot } from '../../core/server/utils';
|
|
||||||
|
|
||||||
function generateUrls({ version, plugin }) {
|
function generateUrls({ version, plugin }) {
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createAbsolutePathSerializer } from '@kbn/dev-utils';
|
import { createAbsolutePathSerializer } from '@kbn/dev-utils';
|
||||||
|
import { fromRoot } from '@kbn/utils';
|
||||||
|
|
||||||
import { fromRoot } from '../../core/server/utils';
|
|
||||||
import { parseMilliseconds, parse } from './settings';
|
import { parseMilliseconds, parse } from './settings';
|
||||||
|
|
||||||
const SECOND = 1000;
|
const SECOND = 1000;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { fromRoot } from '../../core/server/utils';
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { list } from './list';
|
import { list } from './list';
|
||||||
import { Logger } from '../lib/logger';
|
import { Logger } from '../lib/logger';
|
||||||
import { logWarnings } from '../lib/log_warnings';
|
import { logWarnings } from '../lib/log_warnings';
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { fromRoot } from '../../core/server/utils';
|
|
||||||
|
|
||||||
export function parse(command, options) {
|
export function parse(command, options) {
|
||||||
const settings = {
|
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.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getUnusedConfigKeys } from './get_unused_config_keys';
|
import { ConfigService } from '@kbn/config';
|
||||||
import { ConfigService } from '../../config';
|
import { CriticalError } from '../errors';
|
||||||
import { CriticalError } from '../../errors';
|
|
||||||
import { LegacyServiceSetupConfig } from '../types';
|
|
||||||
|
|
||||||
export async function ensureValidConfiguration(
|
export async function ensureValidConfiguration(configService: ConfigService) {
|
||||||
configService: ConfigService,
|
await configService.validate();
|
||||||
{ legacyConfig, settings }: LegacyServiceSetupConfig
|
|
||||||
) {
|
const unusedConfigKeys = await configService.getUnusedPaths();
|
||||||
const unusedConfigKeys = await getUnusedConfigKeys({
|
|
||||||
coreHandledConfigPaths: await configService.getUsedPaths(),
|
|
||||||
settings,
|
|
||||||
legacyConfig,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (unusedConfigKeys.length > 0) {
|
if (unusedConfigKeys.length > 0) {
|
||||||
const message = `Unknown configuration key(s): ${unusedConfigKeys
|
const message = `Unknown configuration key(s): ${unusedConfigKeys
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { coreDeprecationProvider } from './deprecation';
|
export { coreDeprecationProvider } from './deprecation';
|
||||||
|
export { ensureValidConfiguration } from './ensure_valid_configuration';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ConfigService,
|
ConfigService,
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { PackageInfo } from '@kbn/config';
|
import { PackageInfo } from '@kbn/config';
|
||||||
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { distDir as uiSharedDepsDistDir } from '@kbn/ui-shared-deps';
|
import { distDir as uiSharedDepsDistDir } from '@kbn/ui-shared-deps';
|
||||||
import { IRouter } from '../../http';
|
import { IRouter } from '../../http';
|
||||||
import { UiPlugins } from '../../plugins';
|
import { UiPlugins } from '../../plugins';
|
||||||
import { fromRoot } from '../../utils';
|
|
||||||
import { FileHashCache } from './file_hash_cache';
|
import { FileHashCache } from './file_hash_cache';
|
||||||
import { registerRouteForBundle } from './bundles_route';
|
import { registerRouteForBundle } from './bundles_route';
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
|
import { stringify } from 'querystring';
|
||||||
import { Env } from '@kbn/config';
|
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 { InternalCoreSetup } from '../internal_types';
|
||||||
import { CoreContext } from '../core_context';
|
import { CoreContext } from '../core_context';
|
||||||
import { Logger } from '../logging';
|
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) =>
|
router.get({ path: '/core', validate: false }, async (context, req, res) =>
|
||||||
res.ok({ body: { version: '0.0.1' } })
|
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 { hostname } from 'os';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
|
import { ServiceConfigDescriptor } from '../internal_types';
|
||||||
import { CspConfigType, CspConfig, ICspConfig } from '../csp';
|
import { CspConfigType, CspConfig, ICspConfig } from '../csp';
|
||||||
import { ExternalUrlConfig, IExternalUrlConfig } from '../external_url';
|
import { ExternalUrlConfig, IExternalUrlConfig } from '../external_url';
|
||||||
|
|
||||||
|
@ -20,11 +21,7 @@ const hostURISchema = schema.uri({ scheme: ['http', 'https'] });
|
||||||
const match = (regex: RegExp, errorMsg: string) => (str: string) =>
|
const match = (regex: RegExp, errorMsg: string) => (str: string) =>
|
||||||
regex.test(str) ? undefined : errorMsg;
|
regex.test(str) ? undefined : errorMsg;
|
||||||
|
|
||||||
// before update to make sure it's in sync with validation rules in Legacy
|
const configSchema = schema.object(
|
||||||
// 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() }),
|
name: schema.string({ defaultValue: () => hostname() }),
|
||||||
autoListen: schema.boolean({ defaultValue: true }),
|
autoListen: schema.boolean({ defaultValue: true }),
|
||||||
|
@ -152,9 +149,15 @@ export const config = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
),
|
);
|
||||||
|
|
||||||
|
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 {
|
export class HttpConfig implements IHttpConfig {
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {
|
||||||
legacyClusterClientInstanceMock,
|
legacyClusterClientInstanceMock,
|
||||||
} from './core_service.test.mocks';
|
} from './core_service.test.mocks';
|
||||||
|
|
||||||
import Boom from '@hapi/boom';
|
|
||||||
import { Request } from '@hapi/hapi';
|
|
||||||
import { errors as esErrors } from 'elasticsearch';
|
import { errors as esErrors } from 'elasticsearch';
|
||||||
import { LegacyElasticsearchErrorHelpers } from '../../elasticsearch/legacy';
|
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 * as kbnTestServer from '../../../test_helpers/kbn_server';
|
||||||
import { InternalElasticsearchServiceStart } from '../../elasticsearch';
|
import { InternalElasticsearchServiceStart } from '../../elasticsearch';
|
||||||
|
|
||||||
interface User {
|
|
||||||
id: string;
|
|
||||||
roles?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StorageData {
|
|
||||||
value: User;
|
|
||||||
expires: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookieOptions = {
|
const cookieOptions = {
|
||||||
name: 'sid',
|
name: 'sid',
|
||||||
encryptionKey: 'something_at_least_32_characters',
|
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', () => {
|
describe('legacy elasticsearch client', () => {
|
||||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|
|
@ -14,7 +14,7 @@ const mockGetTranslationPaths = getTranslationPaths as jest.Mock;
|
||||||
jest.mock('./get_translation_paths', () => ({
|
jest.mock('./get_translation_paths', () => ({
|
||||||
getTranslationPaths: jest.fn().mockResolvedValue([]),
|
getTranslationPaths: jest.fn().mockResolvedValue([]),
|
||||||
}));
|
}));
|
||||||
jest.mock('../utils', () => ({
|
jest.mock('@kbn/utils', () => ({
|
||||||
fromRoot: jest.fn().mockImplementation((path: string) => path),
|
fromRoot: jest.fn().mockImplementation((path: string) => path),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
import { fromRoot } from '../utils';
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { getTranslationPaths } from './get_translation_paths';
|
import { getTranslationPaths } from './get_translation_paths';
|
||||||
|
|
||||||
export const getKibanaTranslationFiles = async (
|
export const getKibanaTranslationFiles = async (
|
||||||
|
|
|
@ -406,8 +406,6 @@ export type {
|
||||||
SavedObjectsMigrationVersion,
|
SavedObjectsMigrationVersion,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export type { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig } from './legacy';
|
|
||||||
|
|
||||||
export { ServiceStatusLevels } from './status';
|
export { ServiceStatusLevels } from './status';
|
||||||
export type { CoreStatus, ServiceStatus, ServiceStatusLevel, StatusServiceSetup } 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.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @internal */
|
|
||||||
export { ensureValidConfiguration } from './config';
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type { ILegacyService } from './legacy_service';
|
export type { ILegacyService } from './legacy_service';
|
||||||
export { LegacyService } 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 type { PublicMethodsOf } from '@kbn/utility-types';
|
||||||
import { LegacyService } from './legacy_service';
|
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 => ({
|
const createLegacyServiceMock = (): LegacyServiceMock => ({
|
||||||
legacyId: Symbol(),
|
|
||||||
setupLegacyConfig: jest.fn(),
|
|
||||||
setup: jest.fn(),
|
setup: jest.fn(),
|
||||||
start: jest.fn(),
|
|
||||||
stop: jest.fn(),
|
stop: jest.fn(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const createLegacyConfigMock = (): jest.Mocked<LegacyConfig> => ({
|
|
||||||
get: jest.fn(),
|
|
||||||
has: jest.fn(),
|
|
||||||
set: jest.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const legacyServiceMock = {
|
export const legacyServiceMock = {
|
||||||
create: createLegacyServiceMock,
|
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.
|
* 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 { REPO_ROOT } from '@kbn/dev-utils';
|
||||||
|
|
||||||
import KbnServer from '../../../legacy/server/kbn_server';
|
|
||||||
import { Config, Env, ObjectToConfigAdapter } from '../config';
|
import { Config, Env, ObjectToConfigAdapter } from '../config';
|
||||||
import { DiscoveredPlugin } from '../plugins';
|
|
||||||
|
|
||||||
import { getEnvOptions, configServiceMock } from '../config/mocks';
|
import { getEnvOptions, configServiceMock } from '../config/mocks';
|
||||||
import { loggingSystemMock } from '../logging/logging_system.mock';
|
import { loggingSystemMock } from '../logging/logging_system.mock';
|
||||||
import { contextServiceMock } from '../context/context_service.mock';
|
|
||||||
import { httpServiceMock } from '../http/http_service.mock';
|
import { httpServiceMock } from '../http/http_service.mock';
|
||||||
import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock';
|
import { LegacyService, LegacyServiceSetupDeps } from './legacy_service';
|
||||||
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;
|
|
||||||
|
|
||||||
let coreId: symbol;
|
let coreId: symbol;
|
||||||
let env: Env;
|
let env: Env;
|
||||||
|
@ -42,71 +29,16 @@ let config$: BehaviorSubject<Config>;
|
||||||
|
|
||||||
let setupDeps: LegacyServiceSetupDeps;
|
let setupDeps: LegacyServiceSetupDeps;
|
||||||
|
|
||||||
let startDeps: LegacyServiceStartDeps;
|
|
||||||
|
|
||||||
const logger = loggingSystemMock.create();
|
const logger = loggingSystemMock.create();
|
||||||
let configService: ReturnType<typeof configServiceMock.create>;
|
let configService: ReturnType<typeof configServiceMock.create>;
|
||||||
let environmentSetup: ReturnType<typeof environmentServiceMock.createSetupContract>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
coreId = Symbol();
|
coreId = Symbol();
|
||||||
env = Env.createDefault(REPO_ROOT, getEnvOptions());
|
env = Env.createDefault(REPO_ROOT, getEnvOptions());
|
||||||
configService = configServiceMock.create();
|
configService = configServiceMock.create();
|
||||||
environmentSetup = environmentServiceMock.createSetupContract();
|
|
||||||
|
|
||||||
MockKbnServer.prototype.ready = jest.fn().mockReturnValue(Promise.resolve());
|
|
||||||
MockKbnServer.prototype.listen = jest.fn();
|
|
||||||
|
|
||||||
setupDeps = {
|
setupDeps = {
|
||||||
core: {
|
http: httpServiceMock.createInternalSetupContract(),
|
||||||
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: {},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config$ = new BehaviorSubject<Config>(
|
config$ = new BehaviorSubject<Config>(
|
||||||
|
@ -117,78 +49,78 @@ beforeEach(() => {
|
||||||
);
|
);
|
||||||
|
|
||||||
configService.getConfig$.mockReturnValue(config$);
|
configService.getConfig$.mockReturnValue(config$);
|
||||||
configService.getUsedPaths.mockResolvedValue(['foo.bar']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
setupLoggingMock.mockReset();
|
||||||
|
setupLoggingRotateMock.mockReset();
|
||||||
|
reconfigureLoggingMock.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('once LegacyService is set up with connection info', () => {
|
describe('#setup', () => {
|
||||||
test('creates legacy kbnServer and calls `listen`.', async () => {
|
it('initializes legacy logging', async () => {
|
||||||
configService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
|
const opsConfig = {
|
||||||
const legacyService = new LegacyService({
|
interval: moment.duration(5, 'second'),
|
||||||
coreId,
|
};
|
||||||
env,
|
const opsConfig$ = new BehaviorSubject(opsConfig);
|
||||||
logger,
|
|
||||||
configService,
|
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({
|
const legacyService = new LegacyService({
|
||||||
coreId,
|
coreId,
|
||||||
env,
|
env,
|
||||||
logger,
|
logger,
|
||||||
configService: configService as any,
|
configService: configService as any,
|
||||||
});
|
});
|
||||||
await legacyService.setupLegacyConfig();
|
|
||||||
await legacyService.setup(setupDeps);
|
|
||||||
await legacyService.start(startDeps);
|
|
||||||
|
|
||||||
expect(MockKbnServer).toHaveBeenCalledTimes(1);
|
await legacyService.setup(setupDeps);
|
||||||
expect(MockKbnServer).toHaveBeenCalledWith(
|
|
||||||
{ path: { autoListen: false }, server: { autoListen: true } },
|
expect(setupLoggingMock).toHaveBeenCalledTimes(1);
|
||||||
expect.objectContaining({ get: expect.any(Function) }),
|
expect(setupLoggingMock).toHaveBeenCalledWith(
|
||||||
expect.any(Object)
|
setupDeps.http.server,
|
||||||
|
loggingConfig,
|
||||||
|
opsConfig.interval.asMilliseconds()
|
||||||
);
|
);
|
||||||
|
|
||||||
const legacyConfig = MockKbnServer.mock.calls[0][1].get();
|
expect(setupLoggingRotateMock).toHaveBeenCalledTimes(1);
|
||||||
expect(legacyConfig.path.autoListen).toBe(false);
|
expect(setupLoggingRotateMock).toHaveBeenCalledWith(setupDeps.http.server, loggingConfig);
|
||||||
expect(legacyConfig.server.autoListen).toBe(true);
|
});
|
||||||
|
|
||||||
const [mockKbnServer] = MockKbnServer.mock.instances;
|
it('reloads the logging config when the config changes', async () => {
|
||||||
expect(mockKbnServer.ready).toHaveBeenCalledTimes(1);
|
const opsConfig = {
|
||||||
expect(mockKbnServer.listen).not.toHaveBeenCalled();
|
interval: moment.duration(5, 'second'),
|
||||||
expect(mockKbnServer.close).not.toHaveBeenCalled();
|
};
|
||||||
|
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({});
|
||||||
});
|
});
|
||||||
|
|
||||||
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'));
|
|
||||||
const legacyService = new LegacyService({
|
const legacyService = new LegacyService({
|
||||||
coreId,
|
coreId,
|
||||||
env,
|
env,
|
||||||
|
@ -196,19 +128,48 @@ describe('once LegacyService is set up with connection info', () => {
|
||||||
configService: configService as any,
|
configService: configService as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
await legacyService.setupLegacyConfig();
|
|
||||||
await legacyService.setup(setupDeps);
|
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;
|
loggingConfig$.next({
|
||||||
expect(mockKbnServer.listen).toHaveBeenCalled();
|
foo: 'changed',
|
||||||
expect(mockKbnServer.close).toHaveBeenCalled();
|
});
|
||||||
|
|
||||||
|
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(2);
|
||||||
|
expect(reconfigureLoggingMock).toHaveBeenCalledWith(
|
||||||
|
setupDeps.http.server,
|
||||||
|
{ foo: 'changed' },
|
||||||
|
opsConfig.interval.asMilliseconds()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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({});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws if fails to retrieve initial config.', async () => {
|
|
||||||
configService.getConfig$.mockReturnValue(throwError(new Error('something failed')));
|
|
||||||
const legacyService = new LegacyService({
|
const legacyService = new LegacyService({
|
||||||
coreId,
|
coreId,
|
||||||
env,
|
env,
|
||||||
|
@ -216,150 +177,21 @@ describe('once LegacyService is set up with connection info', () => {
|
||||||
configService: configService as any,
|
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`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
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]]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
await legacyService.setup(setupDeps);
|
||||||
|
|
||||||
expect(legacyConfig.get('server.uuid')).toBe('UUID_FROM_SERVICE');
|
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(reconfigureLoggingMock).toHaveBeenCalledWith(
|
||||||
|
setupDeps.http.server,
|
||||||
|
loggingConfig,
|
||||||
|
opsConfig.interval.asMilliseconds()
|
||||||
|
);
|
||||||
|
|
||||||
|
await legacyService.stop();
|
||||||
|
|
||||||
|
loggingConfig$.next({
|
||||||
|
foo: 'changed',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(reconfigureLoggingMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,141 +6,61 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { combineLatest, ConnectableObservable, Observable, Subscription } from 'rxjs';
|
import { combineLatest, Observable, Subscription } from 'rxjs';
|
||||||
import { first, map, publishReplay, tap } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
|
import { Server } from '@hapi/hapi';
|
||||||
import type { PublicMethodsOf } from '@kbn/utility-types';
|
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 {
|
import {
|
||||||
HttpConfig,
|
reconfigureLogging,
|
||||||
HttpConfigType,
|
setupLogging,
|
||||||
config as httpConfig,
|
setupLoggingRotate,
|
||||||
IRouter,
|
LegacyLoggingConfig,
|
||||||
RequestHandlerContextProvider,
|
} from '@kbn/legacy-logging';
|
||||||
} from '../http';
|
|
||||||
|
import { CoreContext } from '../core_context';
|
||||||
|
import { config as loggingConfig } from '../logging';
|
||||||
|
import { opsConfig, OpsConfigType } from '../metrics';
|
||||||
import { Logger } from '../logging';
|
import { Logger } from '../logging';
|
||||||
import { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig, LegacyVars } from './types';
|
import { InternalHttpServiceSetup } from '../http';
|
||||||
import { ExternalUrlConfigType, config as externalUrlConfig } from '../external_url';
|
|
||||||
import { CoreSetup, CoreStart } from '..';
|
|
||||||
|
|
||||||
interface LegacyKbnServer {
|
export interface LegacyServiceSetupDeps {
|
||||||
applyLoggingConfiguration: (settings: Readonly<LegacyVars>) => void;
|
http: InternalHttpServiceSetup;
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export type ILegacyService = PublicMethodsOf<LegacyService>;
|
export type ILegacyService = PublicMethodsOf<LegacyService>;
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export class LegacyService implements CoreService {
|
export class LegacyService {
|
||||||
/** Symbol to represent the legacy platform as a fake "plugin". Used by the ContextService */
|
|
||||||
public readonly legacyId = Symbol();
|
|
||||||
private readonly log: Logger;
|
private readonly log: Logger;
|
||||||
private readonly httpConfig$: Observable<HttpConfig>;
|
private readonly opsConfig$: Observable<OpsConfigType>;
|
||||||
private kbnServer?: LegacyKbnServer;
|
private readonly legacyLoggingConfig$: Observable<LegacyLoggingConfig>;
|
||||||
private configSubscription?: Subscription;
|
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;
|
const { logger, configService } = coreContext;
|
||||||
|
|
||||||
this.log = logger.get('legacy-service');
|
this.log = logger.get('legacy-service');
|
||||||
this.httpConfig$ = combineLatest(
|
this.legacyLoggingConfig$ = configService.atPath<LegacyLoggingConfig>(loggingConfig.path);
|
||||||
configService.atPath<HttpConfigType>(httpConfig.path),
|
this.opsConfig$ = configService.atPath<OpsConfigType>(opsConfig.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!,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setup(setupDeps: LegacyServiceSetupDeps) {
|
public async setup(setupDeps: LegacyServiceSetupDeps) {
|
||||||
this.log.debug('setting up legacy service');
|
this.log.debug('setting up legacy service');
|
||||||
|
await this.setupLegacyLogging(setupDeps.http.server);
|
||||||
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.
|
private async setupLegacyLogging(server: Server) {
|
||||||
this.legacyRawConfig!.set('server.uuid', setupDeps.core.environment.instanceUuid);
|
const legacyLoggingConfig = await this.legacyLoggingConfig$.pipe(first()).toPromise();
|
||||||
|
const currentOpsConfig = await this.opsConfig$.pipe(first()).toPromise();
|
||||||
|
|
||||||
this.setupDeps = setupDeps;
|
await setupLogging(server, legacyLoggingConfig, currentOpsConfig.interval.asMilliseconds());
|
||||||
|
await setupLoggingRotate(server, legacyLoggingConfig);
|
||||||
|
|
||||||
|
this.configSubscription = combineLatest([this.legacyLoggingConfig$, this.opsConfig$]).subscribe(
|
||||||
|
([newLoggingConfig, newOpsConfig]) => {
|
||||||
|
reconfigureLogging(server, newLoggingConfig, newOpsConfig.interval.asMilliseconds());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async start(startDeps: LegacyServiceStartDeps) {
|
|
||||||
const { setupDeps } = this;
|
|
||||||
|
|
||||||
if (!setupDeps || !this.legacyRawConfig) {
|
|
||||||
throw new Error('Legacy service is not setup yet.');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.debug('starting legacy service');
|
|
||||||
|
|
||||||
this.kbnServer = await this.createKbnServer(
|
|
||||||
this.settings!,
|
|
||||||
this.legacyRawConfig!,
|
|
||||||
setupDeps,
|
|
||||||
startDeps
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,156 +71,5 @@ export class LegacyService implements CoreService {
|
||||||
this.configSubscription.unsubscribe();
|
this.configSubscription.unsubscribe();
|
||||||
this.configSubscription = undefined;
|
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 { schema } from '@kbn/config-schema';
|
||||||
import { LegacyLoggingServer } from '@kbn/legacy-logging';
|
import { LegacyLoggingServer } from '@kbn/legacy-logging';
|
||||||
import { DisposableAppender, LogRecord } from '@kbn/logging';
|
import { DisposableAppender, LogRecord } from '@kbn/logging';
|
||||||
import { LegacyVars } from '../../types';
|
|
||||||
|
|
||||||
export interface LegacyAppenderConfig {
|
export interface LegacyAppenderConfig {
|
||||||
type: 'legacy-appender';
|
type: 'legacy-appender';
|
||||||
legacyLoggingConfig?: any;
|
legacyLoggingConfig?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,7 +22,7 @@ export interface LegacyAppenderConfig {
|
||||||
export class LegacyAppender implements DisposableAppender {
|
export class LegacyAppender implements DisposableAppender {
|
||||||
public static configSchema = schema.object({
|
public static configSchema = schema.object({
|
||||||
type: schema.literal('legacy-appender'),
|
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;
|
private readonly loggingServer: LegacyLoggingServer;
|
||||||
|
|
||||||
constructor(legacyLoggingConfig: Readonly<LegacyVars>) {
|
constructor(legacyLoggingConfig: any) {
|
||||||
this.loggingServer = new LegacyLoggingServer(legacyLoggingConfig);
|
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';
|
import { LoggingConfig, config } from './logging_config';
|
||||||
|
|
||||||
test('`schema` creates correct schema with defaults.', () => {
|
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.', () => {
|
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: [],
|
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.', () => {
|
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'],
|
appenders: ['console'],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).toThrowErrorMatchingSnapshot();
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`"[root]: \\"default\\" appender required for migration period till the next major release"`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('`getParentLoggerContext()` returns correct parent context name.', () => {
|
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', () => {
|
describe('extend', () => {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { schema, TypeOf } from '@kbn/config-schema';
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
import { legacyLoggingConfigSchema } from '@kbn/legacy-logging';
|
||||||
import { AppenderConfigType, Appenders } from './appenders/appenders';
|
import { AppenderConfigType, Appenders } from './appenders/appenders';
|
||||||
|
|
||||||
// We need this helper for the types to be correct
|
// 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 type LoggerConfigType = TypeOf<typeof loggerSchema>;
|
||||||
export const config = {
|
export const config = {
|
||||||
path: 'logging',
|
path: 'logging',
|
||||||
schema: schema.object({
|
schema: legacyLoggingConfigSchema.extends({
|
||||||
appenders: schema.mapOf(schema.string(), Appenders.configSchema, {
|
appenders: schema.mapOf(schema.string(), Appenders.configSchema, {
|
||||||
defaultValue: new Map<string, AppenderConfigType>(),
|
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>;
|
appenders: Map<string, AppenderConfigType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ export const loggerContextConfigSchema = schema.object({
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export type LoggerContextConfigType = TypeOf<typeof loggerContextConfigSchema>;
|
export type LoggerContextConfigType = TypeOf<typeof loggerContextConfigSchema>;
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface LoggerContextConfigInput {
|
export interface LoggerContextConfigInput {
|
||||||
// config-schema knows how to handle either Maps or Records
|
// config-schema knows how to handle either Maps or Records
|
||||||
|
|
|
@ -16,6 +16,7 @@ jest.mock('fs', () => ({
|
||||||
const dynamicProps = { process: { pid: expect.any(Number) } };
|
const dynamicProps = { process: { pid: expect.any(Number) } };
|
||||||
|
|
||||||
jest.mock('@kbn/legacy-logging', () => ({
|
jest.mock('@kbn/legacy-logging', () => ({
|
||||||
|
...(jest.requireActual('@kbn/legacy-logging') as any),
|
||||||
setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})),
|
setupLoggingRotate: jest.fn().mockImplementation(() => Promise.resolve({})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -16,3 +16,4 @@ export type {
|
||||||
export type { OpsProcessMetrics, OpsServerMetrics, OpsOsMetrics } from './collectors';
|
export type { OpsProcessMetrics, OpsServerMetrics, OpsOsMetrics } from './collectors';
|
||||||
export { MetricsService } from './metrics_service';
|
export { MetricsService } from './metrics_service';
|
||||||
export { opsConfig } from './ops_config';
|
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 { REPO_ROOT } from '@kbn/utils';
|
||||||
import { loggingSystemMock } from '../logging/logging_system.mock';
|
import { loggingSystemMock } from '../logging/logging_system.mock';
|
||||||
import { duration } from 'moment';
|
import { duration } from 'moment';
|
||||||
import { fromRoot } from '../utils';
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { ByteSizeValue } from '@kbn/config-schema';
|
import { ByteSizeValue } from '@kbn/config-schema';
|
||||||
import { Server } from '../server';
|
import { Server } from '../server';
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import { duration } from 'moment';
|
import { duration } from 'moment';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
import { REPO_ROOT } from '@kbn/dev-utils';
|
import { REPO_ROOT } from '@kbn/dev-utils';
|
||||||
|
import { fromRoot } from '@kbn/utils';
|
||||||
import { createPluginInitializerContext, InstanceInfo } from './plugin_context';
|
import { createPluginInitializerContext, InstanceInfo } from './plugin_context';
|
||||||
import { CoreContext } from '../core_context';
|
import { CoreContext } from '../core_context';
|
||||||
import { Env } from '../config';
|
import { Env } from '../config';
|
||||||
|
@ -16,7 +17,6 @@ import { loggingSystemMock } from '../logging/logging_system.mock';
|
||||||
import { rawConfigServiceMock, getEnvOptions } from '../config/mocks';
|
import { rawConfigServiceMock, getEnvOptions } from '../config/mocks';
|
||||||
import { PluginManifest } from './types';
|
import { PluginManifest } from './types';
|
||||||
import { Server } from '../server';
|
import { Server } from '../server';
|
||||||
import { fromRoot } from '../utils';
|
|
||||||
import { schema, ByteSizeValue } from '@kbn/config-schema';
|
import { schema, ByteSizeValue } from '@kbn/config-schema';
|
||||||
import { ConfigService } from '@kbn/config';
|
import { ConfigService } from '@kbn/config';
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,24 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { schema, TypeOf } from '@kbn/config-schema';
|
import { schema, TypeOf } from '@kbn/config-schema';
|
||||||
|
import { ServiceConfigDescriptor } from '../internal_types';
|
||||||
import { Env } from '../config';
|
import { Env } from '../config';
|
||||||
|
|
||||||
export type PluginsConfigType = TypeOf<typeof config.schema>;
|
const configSchema = schema.object({
|
||||||
|
|
||||||
export const config = {
|
|
||||||
path: 'plugins',
|
|
||||||
schema: schema.object({
|
|
||||||
initialize: schema.boolean({ defaultValue: true }),
|
initialize: schema.boolean({ defaultValue: true }),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an array of directories where another plugin should be loaded from.
|
* Defines an array of directories where another plugin should be loaded from.
|
||||||
*/
|
*/
|
||||||
paths: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
paths: schema.arrayOf(schema.string(), { defaultValue: [] }),
|
||||||
}),
|
});
|
||||||
|
|
||||||
|
export type PluginsConfigType = TypeOf<typeof configSchema>;
|
||||||
|
|
||||||
|
export const config: ServiceConfigDescriptor<PluginsConfigType> = {
|
||||||
|
path: 'plugins',
|
||||||
|
schema: configSchema,
|
||||||
|
deprecations: ({ unusedFromRoot }) => [unusedFromRoot('plugins.scanDirs')],
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
|
|
@ -142,7 +142,6 @@ import { SearchParams } from 'elasticsearch';
|
||||||
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
|
import { SearchResponse as SearchResponse_2 } from 'elasticsearch';
|
||||||
import { SearchShardsParams } from 'elasticsearch';
|
import { SearchShardsParams } from 'elasticsearch';
|
||||||
import { SearchTemplateParams } from 'elasticsearch';
|
import { SearchTemplateParams } from 'elasticsearch';
|
||||||
import { Server } from '@hapi/hapi';
|
|
||||||
import { ShallowPromise } from '@kbn/utility-types';
|
import { ShallowPromise } from '@kbn/utility-types';
|
||||||
import { SnapshotCreateParams } from 'elasticsearch';
|
import { SnapshotCreateParams } from 'elasticsearch';
|
||||||
import { SnapshotCreateRepositoryParams } from 'elasticsearch';
|
import { SnapshotCreateRepositoryParams } from 'elasticsearch';
|
||||||
|
@ -345,7 +344,7 @@ export const config: {
|
||||||
pingTimeout: Type<import("moment").Duration>;
|
pingTimeout: Type<import("moment").Duration>;
|
||||||
logQueries: Type<boolean>;
|
logQueries: Type<boolean>;
|
||||||
ssl: import("@kbn/config-schema").ObjectType<{
|
ssl: import("@kbn/config-schema").ObjectType<{
|
||||||
verificationMode: Type<"certificate" | "none" | "full">;
|
verificationMode: Type<"none" | "certificate" | "full">;
|
||||||
certificateAuthorities: Type<string | string[] | undefined>;
|
certificateAuthorities: Type<string | string[] | undefined>;
|
||||||
certificate: Type<string | undefined>;
|
certificate: Type<string | undefined>;
|
||||||
key: Type<string | undefined>;
|
key: Type<string | undefined>;
|
||||||
|
@ -1305,10 +1304,10 @@ export type KibanaResponseFactory = typeof kibanaResponseFactory;
|
||||||
|
|
||||||
// @public
|
// @public
|
||||||
export const kibanaResponseFactory: {
|
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;
|
message: string | Error;
|
||||||
attributes?: Record<string, any> | undefined;
|
attributes?: Record<string, any> | undefined;
|
||||||
} | Stream | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
} | undefined>(options: CustomHttpResponseOptions<T>) => KibanaResponse<T>;
|
||||||
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||||
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||||
forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse<ResponseError>;
|
||||||
|
@ -1585,20 +1584,6 @@ export class LegacyClusterClient implements ILegacyClusterClient {
|
||||||
close(): void;
|
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)
|
// @public @deprecated (undocumented)
|
||||||
export type LegacyElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log' | 'plugins'> & Pick<ElasticsearchConfig, 'apiVersion' | 'customHeaders' | 'requestHeadersWhitelist' | 'sniffOnStart' | 'sniffOnConnectionFault' | 'hosts' | 'username' | 'password'> & {
|
export type LegacyElasticsearchClientConfig = Pick<ConfigOptions, 'keepAlive' | 'log' | 'plugins'> & Pick<ElasticsearchConfig, 'apiVersion' | 'customHeaders' | 'requestHeadersWhitelist' | 'sniffOnStart' | 'sniffOnConnectionFault' | 'hosts' | 'username' | 'password'> & {
|
||||||
pingTimeout?: ElasticsearchConfig['pingTimeout'] | ConfigOptions['pingTimeout'];
|
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>;
|
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
|
// Warning: (ae-forgotten-export) The symbol "lifecycleResponseFactory" needs to be exported by the entry point index.d.ts
|
||||||
//
|
//
|
||||||
// @public
|
// @public
|
||||||
|
|
|
@ -58,7 +58,7 @@ jest.doMock('./ui_settings/ui_settings_service', () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const mockEnsureValidConfiguration = jest.fn();
|
export const mockEnsureValidConfiguration = jest.fn();
|
||||||
jest.doMock('./legacy/config/ensure_valid_configuration', () => ({
|
jest.doMock('./config/ensure_valid_configuration', () => ({
|
||||||
ensureValidConfiguration: mockEnsureValidConfiguration,
|
ensureValidConfiguration: mockEnsureValidConfiguration,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,6 @@ test('injects legacy dependency to context#setup()', async () => {
|
||||||
pluginDependencies: new Map([
|
pluginDependencies: new Map([
|
||||||
[pluginA, []],
|
[pluginA, []],
|
||||||
[pluginB, [pluginA]],
|
[pluginB, [pluginA]],
|
||||||
[mockLegacyService.legacyId, [pluginA, pluginB]],
|
|
||||||
]),
|
]),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -108,12 +107,10 @@ test('runs services on "start"', async () => {
|
||||||
const server = new Server(rawConfigService, env, logger);
|
const server = new Server(rawConfigService, env, logger);
|
||||||
|
|
||||||
expect(mockHttpService.setup).not.toHaveBeenCalled();
|
expect(mockHttpService.setup).not.toHaveBeenCalled();
|
||||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
await server.setup();
|
await server.setup();
|
||||||
|
|
||||||
expect(mockHttpService.start).not.toHaveBeenCalled();
|
expect(mockHttpService.start).not.toHaveBeenCalled();
|
||||||
expect(mockLegacyService.start).not.toHaveBeenCalled();
|
|
||||||
expect(mockSavedObjectsService.start).not.toHaveBeenCalled();
|
expect(mockSavedObjectsService.start).not.toHaveBeenCalled();
|
||||||
expect(mockUiSettingsService.start).not.toHaveBeenCalled();
|
expect(mockUiSettingsService.start).not.toHaveBeenCalled();
|
||||||
expect(mockMetricsService.start).not.toHaveBeenCalled();
|
expect(mockMetricsService.start).not.toHaveBeenCalled();
|
||||||
|
@ -121,7 +118,6 @@ test('runs services on "start"', async () => {
|
||||||
await server.start();
|
await server.start();
|
||||||
|
|
||||||
expect(mockHttpService.start).toHaveBeenCalledTimes(1);
|
expect(mockHttpService.start).toHaveBeenCalledTimes(1);
|
||||||
expect(mockLegacyService.start).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1);
|
expect(mockSavedObjectsService.start).toHaveBeenCalledTimes(1);
|
||||||
expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1);
|
expect(mockUiSettingsService.start).toHaveBeenCalledTimes(1);
|
||||||
expect(mockMetricsService.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 () => {
|
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(() => {
|
mockEnsureValidConfiguration.mockImplementation(() => {
|
||||||
throw new Error('Unknown configuration keys');
|
throw new Error('Unknown configuration keys');
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,15 +8,20 @@
|
||||||
|
|
||||||
import apm from 'elastic-apm-node';
|
import apm from 'elastic-apm-node';
|
||||||
import { config as pathConfig } from '@kbn/utils';
|
import { config as pathConfig } from '@kbn/utils';
|
||||||
import { mapToObject } from '@kbn/std';
|
import {
|
||||||
import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config';
|
ConfigService,
|
||||||
|
Env,
|
||||||
|
RawConfigurationProvider,
|
||||||
|
coreDeprecationProvider,
|
||||||
|
ensureValidConfiguration,
|
||||||
|
} from './config';
|
||||||
import { CoreApp } from './core_app';
|
import { CoreApp } from './core_app';
|
||||||
import { I18nService } from './i18n';
|
import { I18nService } from './i18n';
|
||||||
import { ElasticsearchService } from './elasticsearch';
|
import { ElasticsearchService } from './elasticsearch';
|
||||||
import { HttpService } from './http';
|
import { HttpService } from './http';
|
||||||
import { HttpResourcesService } from './http_resources';
|
import { HttpResourcesService } from './http_resources';
|
||||||
import { RenderingService } from './rendering';
|
import { RenderingService } from './rendering';
|
||||||
import { LegacyService, ensureValidConfiguration } from './legacy';
|
import { LegacyService } from './legacy';
|
||||||
import { Logger, LoggerFactory, LoggingService, ILoggingSystem } from './logging';
|
import { Logger, LoggerFactory, LoggingService, ILoggingSystem } from './logging';
|
||||||
import { UiSettingsService } from './ui_settings';
|
import { UiSettingsService } from './ui_settings';
|
||||||
import { PluginsService, config as pluginsConfig } from './plugins';
|
import { PluginsService, config as pluginsConfig } from './plugins';
|
||||||
|
@ -121,22 +126,13 @@ export class Server {
|
||||||
const { pluginTree, pluginPaths, uiPlugins } = await this.plugins.discover({
|
const { pluginTree, pluginPaths, uiPlugins } = await this.plugins.discover({
|
||||||
environment: environmentSetup,
|
environment: environmentSetup,
|
||||||
});
|
});
|
||||||
const legacyConfigSetup = await this.legacy.setupLegacyConfig();
|
|
||||||
|
|
||||||
// Immediately terminate in case of invalid configuration
|
// Immediately terminate in case of invalid configuration
|
||||||
// This needs to be done after plugin discovery
|
// This needs to be done after plugin discovery
|
||||||
await this.configService.validate();
|
await ensureValidConfiguration(this.configService);
|
||||||
await ensureValidConfiguration(this.configService, legacyConfigSetup);
|
|
||||||
|
|
||||||
const contextServiceSetup = this.context.setup({
|
const contextServiceSetup = this.context.setup({
|
||||||
// We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins:
|
pluginDependencies: new Map([...pluginTree.asOpaqueIds]),
|
||||||
// 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()]],
|
|
||||||
]),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpSetup = await this.http.setup({
|
const httpSetup = await this.http.setup({
|
||||||
|
@ -222,9 +218,7 @@ export class Server {
|
||||||
this.#pluginsInitialized = pluginsSetup.initialized;
|
this.#pluginsInitialized = pluginsSetup.initialized;
|
||||||
|
|
||||||
await this.legacy.setup({
|
await this.legacy.setup({
|
||||||
core: { ...coreSetup, plugins: pluginsSetup, rendering: renderingSetup },
|
http: httpSetup,
|
||||||
plugins: mapToObject(pluginsSetup.contracts),
|
|
||||||
uiPlugins,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.registerCoreContext(coreSetup);
|
this.registerCoreContext(coreSetup);
|
||||||
|
@ -266,15 +260,7 @@ export class Server {
|
||||||
coreUsageData: coreUsageDataStart,
|
coreUsageData: coreUsageDataStart,
|
||||||
};
|
};
|
||||||
|
|
||||||
const pluginsStart = await this.plugins.start(this.coreStart);
|
await this.plugins.start(this.coreStart);
|
||||||
|
|
||||||
await this.legacy.start({
|
|
||||||
core: {
|
|
||||||
...this.coreStart,
|
|
||||||
plugins: pluginsStart,
|
|
||||||
},
|
|
||||||
plugins: mapToObject(pluginsStart.contracts),
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.http.start();
|
await this.http.start();
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,5 @@ export type {
|
||||||
} from './saved_objects/types';
|
} from './saved_objects/types';
|
||||||
export type { DomainDeprecationDetails, DeprecationsGetResponse } from './deprecations/types';
|
export type { DomainDeprecationDetails, DeprecationsGetResponse } from './deprecations/types';
|
||||||
export * from './ui_settings/types';
|
export * from './ui_settings/types';
|
||||||
export * from './legacy/types';
|
|
||||||
export type { EnvironmentMode, PackageInfo } from '@kbn/config';
|
export type { EnvironmentMode, PackageInfo } from '@kbn/config';
|
||||||
export type { ExternalUrlConfig, IExternalUrlPolicy } from './external_url';
|
export type { ExternalUrlConfig, IExternalUrlPolicy } from './external_url';
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
import { getServices, chance } from './lib';
|
import { getServices, chance } from './lib';
|
||||||
|
|
||||||
export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
async function setup(options: any = {}) {
|
async function setup(options: { initialSettings?: Record<string, any> } = {}) {
|
||||||
const { initialSettings } = options;
|
const { initialSettings } = options;
|
||||||
|
|
||||||
const { kbnServer, uiSettings, callCluster } = getServices();
|
const { uiSettings, callCluster, supertest } = getServices();
|
||||||
|
|
||||||
// delete the kibana index to ensure we start fresh
|
// delete the kibana index to ensure we start fresh
|
||||||
await callCluster('deleteByQuery', {
|
await callCluster('deleteByQuery', {
|
||||||
|
@ -21,31 +21,30 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
conflicts: 'proceed',
|
conflicts: 'proceed',
|
||||||
query: { match_all: {} },
|
query: { match_all: {} },
|
||||||
},
|
},
|
||||||
|
refresh: true,
|
||||||
|
wait_for_completion: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (initialSettings) {
|
if (initialSettings) {
|
||||||
await uiSettings.setMany(initialSettings);
|
await uiSettings.setMany(initialSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { kbnServer, uiSettings };
|
return { uiSettings, supertest };
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('get route', () => {
|
describe('get route', () => {
|
||||||
it('returns a 200 and includes userValues', async () => {
|
it('returns a 200 and includes userValues', async () => {
|
||||||
const defaultIndex = chance.word({ length: 10 });
|
const defaultIndex = chance.word({ length: 10 });
|
||||||
const { kbnServer } = await setup({
|
|
||||||
|
const { supertest } = await setup({
|
||||||
initialSettings: {
|
initialSettings: {
|
||||||
defaultIndex,
|
defaultIndex,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
|
||||||
method: 'GET',
|
|
||||||
url: '/api/kibana/settings',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -64,20 +63,17 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('set route', () => {
|
describe('set route', () => {
|
||||||
it('returns a 200 and all values including update', async () => {
|
it('returns a 200 and all values including update', async () => {
|
||||||
const { kbnServer } = await setup();
|
const { supertest } = await setup();
|
||||||
|
|
||||||
const defaultIndex = chance.word();
|
const defaultIndex = chance.word();
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
|
||||||
method: 'POST',
|
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
|
||||||
url: '/api/kibana/settings/defaultIndex',
|
.send({
|
||||||
payload: {
|
|
||||||
value: defaultIndex,
|
value: defaultIndex,
|
||||||
},
|
})
|
||||||
});
|
.expect(200);
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -94,18 +90,15 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a 400 if trying to set overridden value', async () => {
|
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({
|
const { body } = await supertest('delete', '/api/kibana/settings/foo')
|
||||||
method: 'POST',
|
.send({
|
||||||
url: '/api/kibana/settings/foo',
|
|
||||||
payload: {
|
|
||||||
value: 'baz',
|
value: 'baz',
|
||||||
},
|
})
|
||||||
});
|
.expect(400);
|
||||||
|
|
||||||
expect(statusCode).toBe(400);
|
expect(body).toEqual({
|
||||||
expect(result).toEqual({
|
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
message: 'Unable to update "foo" because it is overridden',
|
message: 'Unable to update "foo" because it is overridden',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -115,22 +108,18 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('setMany route', () => {
|
describe('setMany route', () => {
|
||||||
it('returns a 200 and all values including updates', async () => {
|
it('returns a 200 and all values including updates', async () => {
|
||||||
const { kbnServer } = await setup();
|
const { supertest } = await setup();
|
||||||
|
|
||||||
const defaultIndex = chance.word();
|
const defaultIndex = chance.word();
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('post', '/api/kibana/settings')
|
||||||
method: 'POST',
|
.send({
|
||||||
url: '/api/kibana/settings',
|
|
||||||
payload: {
|
|
||||||
changes: {
|
changes: {
|
||||||
defaultIndex,
|
defaultIndex,
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
});
|
.expect(200);
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -147,20 +136,17 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a 400 if trying to set overridden value', async () => {
|
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({
|
const { body } = await supertest('post', '/api/kibana/settings')
|
||||||
method: 'POST',
|
.send({
|
||||||
url: '/api/kibana/settings',
|
|
||||||
payload: {
|
|
||||||
changes: {
|
changes: {
|
||||||
foo: 'baz',
|
foo: 'baz',
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
});
|
.expect(400);
|
||||||
|
|
||||||
expect(statusCode).toBe(400);
|
expect(body).toEqual({
|
||||||
expect(result).toEqual({
|
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
message: 'Unable to update "foo" because it is overridden',
|
message: 'Unable to update "foo" because it is overridden',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
@ -172,19 +158,15 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
it('returns a 200 and deletes the setting', async () => {
|
it('returns a 200 and deletes the setting', async () => {
|
||||||
const defaultIndex = chance.word({ length: 10 });
|
const defaultIndex = chance.word({ length: 10 });
|
||||||
|
|
||||||
const { kbnServer, uiSettings } = await setup({
|
const { uiSettings, supertest } = await setup({
|
||||||
initialSettings: { defaultIndex },
|
initialSettings: { defaultIndex },
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(await uiSettings.get('defaultIndex')).toBe(defaultIndex);
|
expect(await uiSettings.get('defaultIndex')).toBe(defaultIndex);
|
||||||
|
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
|
||||||
method: 'DELETE',
|
|
||||||
url: '/api/kibana/settings/defaultIndex',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -197,15 +179,11 @@ export const docExistsSuite = (savedObjectsIndex: string) => () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('returns a 400 if deleting overridden value', async () => {
|
it('returns a 400 if deleting overridden value', async () => {
|
||||||
const { kbnServer } = await setup();
|
const { supertest } = await setup();
|
||||||
|
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('delete', '/api/kibana/settings/foo').expect(400);
|
||||||
method: 'DELETE',
|
|
||||||
url: '/api/kibana/settings/foo',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(400);
|
expect(body).toEqual({
|
||||||
expect(result).toEqual({
|
|
||||||
error: 'Bad Request',
|
error: 'Bad Request',
|
||||||
message: 'Unable to update "foo" because it is overridden',
|
message: 'Unable to update "foo" because it is overridden',
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
|
|
|
@ -11,14 +11,7 @@ import { getServices, chance } from './lib';
|
||||||
export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||||
// ensure the kibana index has no documents
|
// ensure the kibana index has no documents
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const { kbnServer, callCluster } = getServices();
|
const { 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
|
// delete all docs from kibana index to ensure savedConfig is not found
|
||||||
await callCluster('deleteByQuery', {
|
await callCluster('deleteByQuery', {
|
||||||
|
@ -31,15 +24,11 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('get route', () => {
|
describe('get route', () => {
|
||||||
it('creates doc, returns a 200 with settings', async () => {
|
it('creates doc, returns a 200 with settings', async () => {
|
||||||
const { kbnServer } = getServices();
|
const { supertest } = getServices();
|
||||||
|
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('get', '/api/kibana/settings').expect(200);
|
||||||
method: 'GET',
|
|
||||||
url: '/api/kibana/settings',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -55,17 +44,17 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('set route', () => {
|
describe('set route', () => {
|
||||||
it('creates doc, returns a 200 with value set', async () => {
|
it('creates doc, returns a 200 with value set', async () => {
|
||||||
const { kbnServer } = getServices();
|
const { supertest } = getServices();
|
||||||
|
|
||||||
const defaultIndex = chance.word();
|
const defaultIndex = chance.word();
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/api/kibana/settings/defaultIndex',
|
|
||||||
payload: { value: defaultIndex },
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
const { body } = await supertest('post', '/api/kibana/settings/defaultIndex')
|
||||||
expect(result).toMatchObject({
|
.send({
|
||||||
|
value: defaultIndex,
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(body).toMatchObject({
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -84,19 +73,17 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('setMany route', () => {
|
describe('setMany route', () => {
|
||||||
it('creates doc, returns 200 with updated values', async () => {
|
it('creates doc, returns 200 with updated values', async () => {
|
||||||
const { kbnServer } = getServices();
|
const { supertest } = getServices();
|
||||||
|
|
||||||
const defaultIndex = chance.word();
|
const defaultIndex = chance.word();
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/api/kibana/settings',
|
|
||||||
payload: {
|
|
||||||
changes: { defaultIndex },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
const { body } = await supertest('post', '/api/kibana/settings')
|
||||||
expect(result).toMatchObject({
|
.send({
|
||||||
|
changes: { defaultIndex },
|
||||||
|
})
|
||||||
|
.expect(200);
|
||||||
|
|
||||||
|
expect(body).toMatchObject({
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
userValue: expect.any(Number),
|
||||||
|
@ -115,15 +102,11 @@ export const docMissingSuite = (savedObjectsIndex: string) => () => {
|
||||||
|
|
||||||
describe('delete route', () => {
|
describe('delete route', () => {
|
||||||
it('creates doc, returns a 200 with just buildNum', async () => {
|
it('creates doc, returns a 200 with just buildNum', async () => {
|
||||||
const { kbnServer } = getServices();
|
const { supertest } = getServices();
|
||||||
|
|
||||||
const { statusCode, result } = await kbnServer.inject({
|
const { body } = await supertest('delete', '/api/kibana/settings/defaultIndex').expect(200);
|
||||||
method: 'DELETE',
|
|
||||||
url: '/api/kibana/settings/defaultIndex',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(statusCode).toBe(200);
|
expect(body).toMatchObject({
|
||||||
expect(result).toMatchObject({
|
|
||||||
settings: {
|
settings: {
|
||||||
buildNum: {
|
buildNum: {
|
||||||
userValue: expect.any(Number),
|
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 { startServers, stopServers } from './lib';
|
||||||
import { docExistsSuite } from './doc_exists';
|
import { docExistsSuite } from './doc_exists';
|
||||||
import { docMissingSuite } from './doc_missing';
|
import { docMissingSuite } from './doc_missing';
|
||||||
import { docMissingAndIndexReadOnlySuite } from './doc_missing_and_index_read_only';
|
|
||||||
|
|
||||||
const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
|
const kibanaVersion = Env.createDefault(REPO_ROOT, getEnvOptions()).packageInfo.version;
|
||||||
const savedObjectIndex = `.kibana_${kibanaVersion}_001`;
|
const savedObjectIndex = `.kibana_${kibanaVersion}_001`;
|
||||||
|
@ -23,7 +22,6 @@ describe('uiSettings/routes', function () {
|
||||||
beforeAll(startServers);
|
beforeAll(startServers);
|
||||||
/* eslint-disable jest/valid-describe */
|
/* eslint-disable jest/valid-describe */
|
||||||
describe('doc missing', docMissingSuite(savedObjectIndex));
|
describe('doc missing', docMissingSuite(savedObjectIndex));
|
||||||
describe('doc missing and index readonly', docMissingAndIndexReadOnlySuite(savedObjectIndex));
|
|
||||||
describe('doc exists', docExistsSuite(savedObjectIndex));
|
describe('doc exists', docExistsSuite(savedObjectIndex));
|
||||||
/* eslint-enable jest/valid-describe */
|
/* eslint-enable jest/valid-describe */
|
||||||
afterAll(stopServers);
|
afterAll(stopServers);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type supertest from 'supertest';
|
||||||
import { SavedObjectsClientContract, IUiSettingsClient } from 'src/core/server';
|
import { SavedObjectsClientContract, IUiSettingsClient } from 'src/core/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -13,6 +14,8 @@ import {
|
||||||
TestElasticsearchUtils,
|
TestElasticsearchUtils,
|
||||||
TestKibanaUtils,
|
TestKibanaUtils,
|
||||||
TestUtils,
|
TestUtils,
|
||||||
|
HttpMethod,
|
||||||
|
getSupertest,
|
||||||
} from '../../../../test_helpers/kbn_server';
|
} from '../../../../test_helpers/kbn_server';
|
||||||
import { LegacyAPICaller } from '../../../elasticsearch/';
|
import { LegacyAPICaller } from '../../../elasticsearch/';
|
||||||
import { httpServerMock } from '../../../http/http_server.mocks';
|
import { httpServerMock } from '../../../http/http_server.mocks';
|
||||||
|
@ -21,13 +24,11 @@ let servers: TestUtils;
|
||||||
let esServer: TestElasticsearchUtils;
|
let esServer: TestElasticsearchUtils;
|
||||||
let kbn: TestKibanaUtils;
|
let kbn: TestKibanaUtils;
|
||||||
|
|
||||||
let kbnServer: TestKibanaUtils['kbnServer'];
|
|
||||||
|
|
||||||
interface AllServices {
|
interface AllServices {
|
||||||
kbnServer: TestKibanaUtils['kbnServer'];
|
|
||||||
savedObjectsClient: SavedObjectsClientContract;
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
callCluster: LegacyAPICaller;
|
callCluster: LegacyAPICaller;
|
||||||
uiSettings: IUiSettingsClient;
|
uiSettings: IUiSettingsClient;
|
||||||
|
supertest: (method: HttpMethod, path: string) => supertest.Test;
|
||||||
}
|
}
|
||||||
|
|
||||||
let services: AllServices;
|
let services: AllServices;
|
||||||
|
@ -47,7 +48,6 @@ export async function startServers() {
|
||||||
});
|
});
|
||||||
esServer = await servers.startES();
|
esServer = await servers.startES();
|
||||||
kbn = await servers.startKibana();
|
kbn = await servers.startKibana();
|
||||||
kbnServer = kbn.kbnServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getServices() {
|
export function getServices() {
|
||||||
|
@ -61,12 +61,10 @@ export function getServices() {
|
||||||
httpServerMock.createKibanaRequest()
|
httpServerMock.createKibanaRequest()
|
||||||
);
|
);
|
||||||
|
|
||||||
const uiSettings = kbnServer.newPlatform.start.core.uiSettings.asScopedToClient(
|
const uiSettings = kbn.coreStart.uiSettings.asScopedToClient(savedObjectsClient);
|
||||||
savedObjectsClient
|
|
||||||
);
|
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
kbnServer,
|
supertest: (method: HttpMethod, path: string) => getSupertest(kbn.root, method, path),
|
||||||
callCluster,
|
callCluster,
|
||||||
savedObjectsClient,
|
savedObjectsClient,
|
||||||
uiSettings,
|
uiSettings,
|
||||||
|
@ -77,7 +75,6 @@ export function getServices() {
|
||||||
|
|
||||||
export async function stopServers() {
|
export async function stopServers() {
|
||||||
services = null!;
|
services = null!;
|
||||||
kbnServer = null!;
|
|
||||||
if (servers) {
|
if (servers) {
|
||||||
await esServer.stop();
|
await esServer.stop();
|
||||||
await kbn.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 { BehaviorSubject } from 'rxjs';
|
||||||
import supertest from 'supertest';
|
import supertest from 'supertest';
|
||||||
|
|
||||||
import { CoreStart } from 'src/core/server';
|
import { InternalCoreSetup, InternalCoreStart } from '../server/internal_types';
|
||||||
import { LegacyAPICaller } from '../server/elasticsearch';
|
import { LegacyAPICaller } from '../server/elasticsearch';
|
||||||
import { CliArgs, Env } from '../server/config';
|
import { CliArgs, Env } from '../server/config';
|
||||||
import { Root } from '../server/root';
|
import { Root } from '../server/root';
|
||||||
import KbnServer from '../../legacy/server/kbn_server';
|
|
||||||
|
|
||||||
export type HttpMethod = 'delete' | 'get' | 'head' | 'post' | 'put';
|
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<
|
export const request: Record<
|
||||||
HttpMethod,
|
HttpMethod,
|
||||||
(root: Root, path: string) => ReturnType<typeof getSupertest>
|
(root: Root, path: string) => ReturnType<typeof getSupertest>
|
||||||
|
@ -164,8 +155,8 @@ export interface TestElasticsearchUtils {
|
||||||
|
|
||||||
export interface TestKibanaUtils {
|
export interface TestKibanaUtils {
|
||||||
root: Root;
|
root: Root;
|
||||||
coreStart: CoreStart;
|
coreSetup: InternalCoreSetup;
|
||||||
kbnServer: KbnServer;
|
coreStart: InternalCoreStart;
|
||||||
stop: () => Promise<void>;
|
stop: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,14 +274,12 @@ export function createTestServers({
|
||||||
startKibana: async () => {
|
startKibana: async () => {
|
||||||
const root = createRootWithCorePlugins(kbnSettings);
|
const root = createRootWithCorePlugins(kbnSettings);
|
||||||
|
|
||||||
await root.setup();
|
const coreSetup = await root.setup();
|
||||||
const coreStart = await root.start();
|
const coreStart = await root.start();
|
||||||
|
|
||||||
const kbnServer = getKbnServer(root);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
root,
|
root,
|
||||||
kbnServer,
|
coreSetup,
|
||||||
coreStart,
|
coreStart,
|
||||||
stop: async () => await root.shutdown(),
|
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