Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a7d5e97
[NP] Expose global config to the plugins
afharo Nov 20, 2019
395f26f
Merge branch 'master' into np-expose-global-config
elasticmachine Nov 25, 2019
7ea1c50
globalConfig in Plugin context: expose read-only methods only
afharo Nov 25, 2019
d9ec3d3
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 26, 2019
4e31b77
SharedGlobalConfig rework + Moving pkg, fromRoot & path utils from le…
afharo Nov 27, 2019
88bc0b1
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 27, 2019
592e935
Updated API docs
afharo Nov 27, 2019
db6b130
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 27, 2019
5f7c0e3
Fix test references to the moved utils
afharo Nov 27, 2019
7b7ea57
Replace zip with combineLatest
afharo Nov 28, 2019
82b62c8
Change tests to describe/it + remove "(deprecated)" from the test des…
afharo Nov 29, 2019
6caab53
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Nov 29, 2019
dc9b692
Moving path files to a folder + exposing the config path in the contract
afharo Nov 29, 2019
e341ee2
deepFreeze the globalConfig in the pluginContext
afharo Nov 29, 2019
edd8760
Fix types in tests with new path.config
afharo Nov 29, 2019
03ecab3
Merge branch 'master' into np-expose-global-config
elasticmachine Dec 2, 2019
3502f9a
Move fromRoot and package_json utils to core/server/utils
afharo Dec 2, 2019
146161a
Rename globalConfig to legacy.globalConfig$
afharo Dec 2, 2019
8f67cc9
path.config renamed to path.configDir (not renaming path.data because…
afharo Dec 2, 2019
e729d5e
Change configDir in mocker as well
afharo Dec 2, 2019
2bc7916
Fix test after config renamed to configDir
afharo Dec 2, 2019
684e89d
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 3, 2019
83f1d80
Fix API docs conflicts
afharo Dec 3, 2019
23a34f8
Rename the path properties when exposing them
afharo Dec 3, 2019
4847fe9
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 3, 2019
4adebd5
path.configDir removed from the path config-schema
afharo Dec 3, 2019
31cbab2
Remove path.configDir. It is already in env.configs
afharo Dec 3, 2019
535f50e
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 4, 2019
79b2133
Add Migration documentation and examples
afharo Dec 4, 2019
5951986
Merge branch 'master' of github.com:elastic/kibana into np-expose-glo…
afharo Dec 4, 2019
9337633
Fix 'kibana/server' imports in the MIGRATION docs
afharo Dec 4, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ Search for objects
<b>Signature:</b>

```typescript
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectFindOptionsServer, "search" | "filter" | "type" | "page" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
```
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export declare class SavedObjectsClient
| [bulkGet](./kibana-plugin-public.savedobjectsclient.bulkget.md) | | <code>(objects?: {</code><br/><code> id: string;</code><br/><code> type: string;</code><br/><code> }[]) =&gt; Promise&lt;SavedObjectsBatchResponse&lt;SavedObjectAttributes&gt;&gt;</code> | Returns an array of objects by id |
| [create](./kibana-plugin-public.savedobjectsclient.create.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, attributes: T, options?: SavedObjectsCreateOptions) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Persists an object |
| [delete](./kibana-plugin-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string) =&gt; Promise&lt;{}&gt;</code> | Deletes an object |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;hasReference&quot; &#124; &quot;sortField&quot; &#124; &quot;page&quot; &#124; &quot;perPage&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [find](./kibana-plugin-public.savedobjectsclient.find.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(options: Pick&lt;SavedObjectFindOptionsServer, &quot;search&quot; &#124; &quot;filter&quot; &#124; &quot;type&quot; &#124; &quot;page&quot; &#124; &quot;fields&quot; &#124; &quot;searchFields&quot; &#124; &quot;defaultSearchOperator&quot; &#124; &quot;hasReference&quot; &#124; &quot;sortField&quot; &#124; &quot;perPage&quot;&gt;) =&gt; Promise&lt;SavedObjectsFindResponsePublic&lt;T&gt;&gt;</code> | Search for objects |
| [get](./kibana-plugin-public.savedobjectsclient.get.md) | | <code>&lt;T extends SavedObjectAttributes&gt;(type: string, id: string) =&gt; Promise&lt;SimpleSavedObject&lt;T&gt;&gt;</code> | Fetches a single object |

## Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

```typescript
config: {
legacy: {
globalConfig$: Observable<SharedGlobalConfig>;
};
create: <T = ConfigSchema>() => Observable<T>;
createIfExists: <T = ConfigSchema>() => Observable<T | undefined>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface PluginInitializerContext<ConfigSchema = unknown>

| Property | Type | Description |
| --- | --- | --- |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [config](./kibana-plugin-server.plugininitializercontext.config.md) | <code>{</code><br/><code> legacy: {</code><br/><code> globalConfig$: Observable&lt;SharedGlobalConfig&gt;;</code><br/><code> };</code><br/><code> create: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T&gt;;</code><br/><code> createIfExists: &lt;T = ConfigSchema&gt;() =&gt; Observable&lt;T &#124; undefined&gt;;</code><br/><code> }</code> | |
| [env](./kibana-plugin-server.plugininitializercontext.env.md) | <code>{</code><br/><code> mode: EnvironmentMode;</code><br/><code> packageInfo: Readonly&lt;PackageInfo&gt;;</code><br/><code> }</code> | |
| [logger](./kibana-plugin-server.plugininitializercontext.logger.md) | <code>LoggerFactory</code> | |
| [opaqueId](./kibana-plugin-server.plugininitializercontext.opaqueid.md) | <code>PluginOpaqueId</code> | |
Expand Down
2 changes: 1 addition & 1 deletion src/cli/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import _ from 'lodash';
import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from './command';
import serveCommand from './serve/serve';

Expand Down
2 changes: 1 addition & 1 deletion src/cli/cluster/cluster_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export default class ClusterManager {

setupWatching(extraPaths, pluginInternalDirsIgnore) {
const chokidar = require('chokidar');
const { fromRoot } = require('../../legacy/utils');
const { fromRoot } = require('../../core/server/utils');

const watchPaths = [
fromRoot('src/core'),
Expand Down
3 changes: 2 additions & 1 deletion src/cli/cluster/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import _ from 'lodash';
import cluster from 'cluster';
import { EventEmitter } from 'events';

import { BinderFor, fromRoot } from '../../legacy/utils';
import { BinderFor } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';

const cliPath = fromRoot('src/cli');
const baseArgs = _.difference(process.argv.slice(2), ['--no-watch']);
Expand Down
4 changes: 2 additions & 2 deletions src/cli/serve/read_keystore.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import path from 'path';
import { set } from 'lodash';

import { Keystore } from '../../legacy/server/keystore';
import { getData } from '../../legacy/server/path';
import { getDataPath } from '../../core/server/path';

export function readKeystore(dataPath = getData()) {
export function readKeystore(dataPath = getDataPath()) {
const keystore = new Keystore(path.join(dataPath, 'kibana.keystore'));
keystore.load();

Expand Down
7 changes: 4 additions & 3 deletions src/cli/serve/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { statSync } from 'fs';
import { resolve } from 'path';
import url from 'url';

import { fromRoot, IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { getConfig } from '../../legacy/server/path';
import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { getConfigPath } from '../../core/server/path';
import { bootstrap } from '../../core/server';
import { readKeystore } from './read_keystore';

Expand Down Expand Up @@ -166,7 +167,7 @@ export default function (program) {
'-c, --config <path>',
'Path to the config file, use multiple --config args to include multiple config files',
configPathCollector,
[ getConfig() ]
[ getConfigPath() ]
)
.option('-p, --port <port>', 'The port to bind to', parseInt)
.option('-q, --quiet', 'Prevent all logging except errors')
Expand Down
6 changes: 3 additions & 3 deletions src/cli_keystore/cli_keystore.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@

import { join } from 'path';

import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from '../cli/command';
import { getData } from '../legacy/server/path';
import { getDataPath } from '../core/server/path';
import { Keystore } from '../legacy/server/keystore';

const path = join(getData(), 'kibana.keystore');
const path = join(getDataPath(), 'kibana.keystore');
const keystore = new Keystore(path);

import { createCli } from './create';
Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import _ from 'lodash';
import { pkg } from '../legacy/utils';
import { pkg } from '../core/server/utils';
import Command from '../cli/command';
import listCommand from './list';
import installCommand from './install';
Expand Down
6 changes: 3 additions & 3 deletions src/cli_plugin/install/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
* under the License.
*/

import { fromRoot, pkg } from '../../legacy/utils';
import { fromRoot, pkg } from '../../core/server/utils';
import install from './install';
import Logger from '../lib/logger';
import { getConfig } from '../../legacy/server/path';
import { getConfigPath } from '../../core/server/path';
import { parse, parseMilliseconds } from './settings';
import logWarnings from '../lib/log_warnings';
import { warnIfUsingPluginDirOption } from '../lib/warn_if_plugin_dir_option';
Expand Down Expand Up @@ -50,7 +50,7 @@ export default function pluginInstall(program) {
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
getConfigPath()
)
.option(
'-t, --timeout <duration>',
Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/install/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { resolve } from 'path';
import { parseMilliseconds, parse } from './settings';

Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import list from './list';
import Logger from '../lib/logger';
import { parse } from './settings';
Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/list/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { parse } from './settings';

describe('kibana cli', function () {
Expand Down
6 changes: 3 additions & 3 deletions src/cli_plugin/remove/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
* under the License.
*/

import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import remove from './remove';
import Logger from '../lib/logger';
import { parse } from './settings';
import { getConfig } from '../../legacy/server/path';
import { getConfigPath } from '../../core/server/path';
import logWarnings from '../lib/log_warnings';
import { warnIfUsingPluginDirOption } from '../lib/warn_if_plugin_dir_option';

Expand Down Expand Up @@ -50,7 +50,7 @@ export default function pluginRemove(program) {
.option(
'-c, --config <path>',
'path to the config file',
getConfig()
getConfigPath()
)
.option(
'-d, --plugin-dir <path>',
Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/remove/settings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { fromRoot } from '../../legacy/utils';
import { fromRoot } from '../../core/server/utils';
import { parse } from './settings';

describe('kibana cli', function () {
Expand Down
2 changes: 1 addition & 1 deletion src/core/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ export class SavedObjectsClient {
bulkUpdate<T extends SavedObjectAttributes>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<SavedObjectAttributes>>;
create: <T extends SavedObjectAttributes>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>;
delete: (type: string, id: string) => Promise<{}>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "page" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
find: <T extends SavedObjectAttributes>(options: Pick<SavedObjectsFindOptions, "search" | "filter" | "type" | "page" | "fields" | "searchFields" | "defaultSearchOperator" | "hasReference" | "sortField" | "perPage">) => Promise<SavedObjectsFindResponsePublic<T>>;
get: <T extends SavedObjectAttributes>(type: string, id: string) => Promise<SimpleSavedObject<T>>;
update<T extends SavedObjectAttributes>(type: string, id: string, attributes: T, { version, migrationVersion, references }?: SavedObjectsUpdateOptions): Promise<SimpleSavedObject<T>>;
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions src/core/server/legacy/legacy_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ describe('once LegacyService is set up with connection info', () => {

expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: { autoListen: true }, server: { autoListen: true } },
{ path: { autoListen: true }, server: { autoListen: true } }, // Because of the mock, path also gets the value
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
Expand All @@ -159,8 +159,8 @@ describe('once LegacyService is set up with connection info', () => {

expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: { autoListen: false }, server: { autoListen: true } },
{ path: { autoListen: false }, server: { autoListen: true } },
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
Expand Down Expand Up @@ -296,8 +296,8 @@ describe('once LegacyService is set up without connection info', () => {
test('creates legacy kbnServer with `autoListen: false`.', () => {
expect(MockKbnServer).toHaveBeenCalledTimes(1);
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{ path: {}, server: { autoListen: true } },
{ path: {}, server: { autoListen: true } },
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
Expand Down
23 changes: 15 additions & 8 deletions src/core/server/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { Logger } from '../logging';
import { PluginsServiceSetup, PluginsServiceStart } from '../plugins';
import { findLegacyPluginSpecs } from './plugins';
import { LegacyPluginSpec } from './plugins/find_legacy_plugin_specs';
import { PathConfigType } from '../path';
import { LegacyConfig } from './config';

interface LegacyKbnServer {
Expand All @@ -40,7 +41,7 @@ interface LegacyKbnServer {
close: () => Promise<void>;
}

function getLegacyRawConfig(config: Config) {
function getLegacyRawConfig(config: Config, pathConfig: PathConfigType) {
const rawConfig = config.toRaw();

// Elasticsearch config is solely handled by the core and legacy platform
Expand All @@ -49,7 +50,10 @@ function getLegacyRawConfig(config: Config) {
delete rawConfig.elasticsearch;
}

return rawConfig;
return {
...rawConfig,
path: pathConfig, // 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
};
}

/**
Expand Down Expand Up @@ -96,7 +100,7 @@ export class LegacyService implements CoreService {
private kbnServer?: LegacyKbnServer;
private configSubscription?: Subscription;
private setupDeps?: LegacyServiceSetupDeps;
private update$: ConnectableObservable<Config> | undefined;
private update$: ConnectableObservable<[Config, PathConfigType]> | undefined;
private legacyRawConfig: LegacyConfig | undefined;
private legacyPlugins:
| {
Expand All @@ -118,22 +122,25 @@ export class LegacyService implements CoreService {
}

public async discoverPlugins(): Promise<LegacyServiceDiscoverPlugins> {
this.update$ = this.coreContext.configService.getConfig$().pipe(
tap(config => {
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));
this.kbnServer.applyLoggingConfiguration(getLegacyRawConfig(config, pathConfig));
}
}),
tap({ error: err => this.log.error(err) }),
publishReplay(1)
) as ConnectableObservable<Config>;
) as ConnectableObservable<[Config, PathConfigType]>;

this.configSubscription = this.update$.connect();

this.settings = await this.update$
.pipe(
first(),
map(config => getLegacyRawConfig(config))
map(([config, pathConfig]) => getLegacyRawConfig(config, pathConfig))
)
.toPromise();

Expand Down
17 changes: 17 additions & 0 deletions src/core/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
* under the License.
*/
import { of } from 'rxjs';
import { duration } from 'moment';
import { PluginInitializerContext, CoreSetup, CoreStart } from '.';
import { loggingServiceMock } from './logging/logging_service.mock';
import { elasticsearchServiceMock } from './elasticsearch/elasticsearch_service.mock';
import { httpServiceMock } from './http/http_service.mock';
import { contextServiceMock } from './context/context_service.mock';
import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock';
import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';
import { SharedGlobalConfig } from './plugins';
import { InternalCoreSetup, InternalCoreStart } from './internal_types';
import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock';

Expand All @@ -37,7 +39,22 @@ export { savedObjectsClientMock } from './saved_objects/service/saved_objects_cl
export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock';

export function pluginInitializerContextConfigMock<T>(config: T) {
const globalConfig: SharedGlobalConfig = {
kibana: { defaultAppId: 'home-mocks', index: '.kibana-tests' },
elasticsearch: {
shardTimeout: duration('30s'),
requestTimeout: duration('30s'),
pingTimeout: duration('30s'),
startupTimeout: duration('30s'),
},
path: {
configDir: '/tmp',
dataDir: '/tmp',
},
};

const mock: jest.Mocked<PluginInitializerContext<T>['config']> = {
legacy: { globalConfig$: of(globalConfig) },
create: jest.fn().mockReturnValue(of(config)),
createIfExists: jest.fn().mockReturnValue(of(config)),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
* under the License.
*/

import { getConfig, getData } from './';
import { accessSync, R_OK } from 'fs';
import { accessSync, constants } from 'fs';
import { getConfigPath, getDataPath } from './';

describe('Default path finder', function () {
describe('Default path finder', () => {
it('should find a kibana.yml', () => {
const configPath = getConfig();
expect(() => accessSync(configPath, R_OK)).not.toThrow();
const configPath = getConfigPath();
expect(() => accessSync(configPath, constants.R_OK)).not.toThrow();
});

it('should find a data directory', () => {
const dataPath = getData();
expect(() => accessSync(dataPath, R_OK)).not.toThrow();
const dataPath = getDataPath();
expect(() => accessSync(dataPath, constants.R_OK)).not.toThrow();
});
});
Loading