Fix running Kibana Platform migrations in development (#61325) (#61360)

This commit is contained in:
Josh Dover 2020-03-25 17:37:33 -06:00 committed by GitHub
parent 677dfc6567
commit 4a2a616dd7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 71 additions and 117 deletions

View file

@ -115,8 +115,6 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PluginConfigDescriptor](./kibana-plugin-core-server.pluginconfigdescriptor.md) | Describes a plugin configuration properties. |
| [PluginInitializerContext](./kibana-plugin-core-server.plugininitializercontext.md) | Context that's available to plugins during initialization stage. |
| [PluginManifest](./kibana-plugin-core-server.pluginmanifest.md) | Describes the set of required and optional properties plugin can define in its mandatory JSON manifest file. |
| [PluginsServiceSetup](./kibana-plugin-core-server.pluginsservicesetup.md) | |
| [PluginsServiceStart](./kibana-plugin-core-server.pluginsservicestart.md) | |
| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.<!-- -->Provides the following clients and services: - [rendering](./kibana-plugin-core-server.iscopedrenderingclient.md) - Rendering client which uses the data of the incoming request - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.dataClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [elasticsearch.adminClient](./kibana-plugin-core-server.scopedclusterclient.md) - Elasticsearch admin client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
| [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. |
| [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. |

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginsServiceSetup](./kibana-plugin-core-server.pluginsservicesetup.md) &gt; [contracts](./kibana-plugin-core-server.pluginsservicesetup.contracts.md)
## PluginsServiceSetup.contracts property
<b>Signature:</b>
```typescript
contracts: Map<PluginName, unknown>;
```

View file

@ -1,20 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginsServiceSetup](./kibana-plugin-core-server.pluginsservicesetup.md)
## PluginsServiceSetup interface
<b>Signature:</b>
```typescript
export interface PluginsServiceSetup
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [contracts](./kibana-plugin-core-server.pluginsservicesetup.contracts.md) | <code>Map&lt;PluginName, unknown&gt;</code> | |
| [uiPlugins](./kibana-plugin-core-server.pluginsservicesetup.uiplugins.md) | <code>{</code><br/><code> internal: Map&lt;PluginName, InternalPluginInfo&gt;;</code><br/><code> public: Map&lt;PluginName, DiscoveredPlugin&gt;;</code><br/><code> browserConfigs: Map&lt;PluginName, Observable&lt;unknown&gt;&gt;;</code><br/><code> }</code> | |

View file

@ -1,15 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginsServiceSetup](./kibana-plugin-core-server.pluginsservicesetup.md) &gt; [uiPlugins](./kibana-plugin-core-server.pluginsservicesetup.uiplugins.md)
## PluginsServiceSetup.uiPlugins property
<b>Signature:</b>
```typescript
uiPlugins: {
internal: Map<PluginName, InternalPluginInfo>;
public: Map<PluginName, DiscoveredPlugin>;
browserConfigs: Map<PluginName, Observable<unknown>>;
};
```

View file

@ -1,11 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginsServiceStart](./kibana-plugin-core-server.pluginsservicestart.md) &gt; [contracts](./kibana-plugin-core-server.pluginsservicestart.contracts.md)
## PluginsServiceStart.contracts property
<b>Signature:</b>
```typescript
contracts: Map<PluginName, unknown>;
```

View file

@ -1,19 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [PluginsServiceStart](./kibana-plugin-core-server.pluginsservicestart.md)
## PluginsServiceStart interface
<b>Signature:</b>
```typescript
export interface PluginsServiceStart
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [contracts](./kibana-plugin-core-server.pluginsservicestart.contracts.md) | <code>Map&lt;PluginName, unknown&gt;</code> | |

View file

@ -87,6 +87,7 @@ beforeEach(() => {
},
savedObjects: savedObjectsServiceMock.createInternalSetupContract(),
plugins: {
initialized: true,
contracts: new Map([['plugin-id', 'plugin-value']]),
uiPlugins: {
public: new Map([['plugin-id', {} as DiscoveredPlugin]]),

View file

@ -28,6 +28,7 @@ const createSetupContractMock = (): PluginsServiceSetup => ({
internal: new Map(),
public: new Map(),
},
initialized: true,
});
const createStartContractMock = () => ({ contracts: new Map() });
const createServiceMock = (): PluginsServiceMock => ({

View file

@ -516,28 +516,29 @@ describe('PluginsService', () => {
});
describe('#setup()', () => {
beforeEach(() => {
mockDiscover.mockReturnValue({
error$: from([]),
plugin$: from([
createPlugin('plugin-1', {
path: 'path-1',
version: 'some-version',
configPath: 'plugin1',
}),
createPlugin('plugin-2', {
path: 'path-2',
version: 'some-version',
configPath: 'plugin2',
}),
]),
});
mockPluginSystem.uiPlugins.mockReturnValue(new Map());
});
describe('uiPlugins.internal', () => {
it('includes disabled plugins', async () => {
mockDiscover.mockReturnValue({
error$: from([]),
plugin$: from([
createPlugin('plugin-1', {
path: 'path-1',
version: 'some-version',
configPath: 'plugin1',
}),
createPlugin('plugin-2', {
path: 'path-2',
version: 'some-version',
configPath: 'plugin2',
}),
]),
});
mockPluginSystem.uiPlugins.mockReturnValue(new Map());
config$.next({ plugins: { initialize: true }, plugin1: { enabled: false } });
await pluginsService.discover();
const { uiPlugins } = await pluginsService.setup({} as any);
expect(uiPlugins.internal).toMatchInlineSnapshot(`
@ -552,6 +553,24 @@ describe('PluginsService', () => {
`);
});
});
describe('plugin initialization', () => {
it('does initialize if plugins.initialize is true', async () => {
config$.next({ plugins: { initialize: true } });
await pluginsService.discover();
const { initialized } = await pluginsService.setup({} as any);
expect(mockPluginSystem.setupPlugins).toHaveBeenCalled();
expect(initialized).toBe(true);
});
it('does not initialize if plugins.initialize is false', async () => {
config$.next({ plugins: { initialize: false } });
await pluginsService.discover();
const { initialized } = await pluginsService.setup({} as any);
expect(mockPluginSystem.setupPlugins).not.toHaveBeenCalled();
expect(initialized).toBe(false);
});
});
});
describe('#stop()', () => {

View file

@ -33,8 +33,11 @@ import { InternalCoreSetup, InternalCoreStart } from '../internal_types';
import { IConfigService } from '../config';
import { pick } from '../../utils';
/** @public */
/** @internal */
export interface PluginsServiceSetup {
/** Indicates whether or not plugins were initialized. */
initialized: boolean;
/** Setup contracts returned by plugins. */
contracts: Map<PluginName, unknown>;
uiPlugins: {
/**
@ -55,8 +58,9 @@ export interface PluginsServiceSetup {
};
}
/** @public */
/** @internal */
export interface PluginsServiceStart {
/** Start contracts returned by plugins. */
contracts: Map<PluginName, unknown>;
}
@ -103,14 +107,16 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
const config = await this.config$.pipe(first()).toPromise();
let contracts = new Map<PluginName, unknown>();
if (!config.initialize || this.coreContext.env.isDevClusterMaster) {
this.log.info('Plugin initialization disabled.');
} else {
const initialize = config.initialize && !this.coreContext.env.isDevClusterMaster;
if (initialize) {
contracts = await this.pluginsSystem.setupPlugins(deps);
} else {
this.log.info('Plugin initialization disabled.');
}
const uiPlugins = this.pluginsSystem.uiPlugins();
return {
initialized: initialize,
contracts,
uiPlugins: {
internal: this.uiPluginInternalInfo,

View file

@ -171,14 +171,12 @@ describe('SavedObjectsService', () => {
return expect(KibanaMigratorMock.mock.calls[0][0].callCluster()).resolves.toMatch('success');
});
it('skips KibanaMigrator migrations when --optimize=true', async () => {
const coreContext = createCoreContext({
env: ({ cliArgs: { optimize: true }, packageInfo: { version: 'x.x.x' } } as unknown) as Env,
});
it('skips KibanaMigrator migrations when pluginsInitialized=false', async () => {
const coreContext = createCoreContext({ skipMigration: false });
const soService = new SavedObjectsService(coreContext);
await soService.setup(createSetupDeps());
await soService.start({});
await soService.start({ pluginsInitialized: false });
expect(migratorInstanceMock.runMigrations).not.toHaveBeenCalled();
});

View file

@ -269,7 +269,9 @@ interface WrappedClientFactoryWrapper {
/** @internal */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SavedObjectsStartDeps {}
export interface SavedObjectsStartDeps {
pluginsInitialized?: boolean;
}
export class SavedObjectsService
implements CoreService<InternalSavedObjectsServiceSetup, InternalSavedObjectsServiceStart> {
@ -349,7 +351,7 @@ export class SavedObjectsService
}
public async start(
core: SavedObjectsStartDeps,
{ pluginsInitialized = true }: SavedObjectsStartDeps,
migrationsRetryDelay?: number
): Promise<InternalSavedObjectsServiceStart> {
if (!this.setupDeps || !this.config) {
@ -376,9 +378,11 @@ export class SavedObjectsService
* However, our build system optimize step and some tests depend on the
* HTTP server running without an Elasticsearch server being available.
* So, when the `migrations.skip` is true, we skip migrations altogether.
*
* We also cannot safely run migrations if plugins are not initialized since
* not plugin migrations won't be registered.
*/
const cliArgs = this.coreContext.env.cliArgs;
const skipMigrations = cliArgs.optimize || this.config.migration.skip;
const skipMigrations = this.config.migration.skip || !pluginsInitialized;
if (skipMigrations) {
this.logger.warn(

View file

@ -1426,10 +1426,10 @@ export type PluginName = string;
// @public (undocumented)
export type PluginOpaqueId = symbol;
// @public (undocumented)
// @internal (undocumented)
export interface PluginsServiceSetup {
// (undocumented)
contracts: Map<PluginName, unknown>;
initialized: boolean;
// (undocumented)
uiPlugins: {
internal: Map<PluginName, InternalPluginInfo>;
@ -1438,9 +1438,8 @@ export interface PluginsServiceSetup {
};
}
// @public (undocumented)
// @internal (undocumented)
export interface PluginsServiceStart {
// (undocumented)
contracts: Map<PluginName, unknown>;
}
@ -2348,7 +2347,7 @@ export const validBodyOutput: readonly ["data", "stream"];
// src/core/server/legacy/types.ts:164:3 - (ae-forgotten-export) The symbol "LegacyNavLinkSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:165:3 - (ae-forgotten-export) The symbol "LegacyAppSpec" needs to be exported by the entry point index.d.ts
// src/core/server/legacy/types.ts:166:16 - (ae-forgotten-export) The symbol "LegacyPluginSpec" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/plugins_service.ts:44:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/plugins_service.ts:47:5 - (ae-forgotten-export) The symbol "InternalPluginInfo" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "KibanaConfigType" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:226:3 - (ae-forgotten-export) The symbol "SharedGlobalConfigKeys" needs to be exported by the entry point index.d.ts
// src/core/server/plugins/types.ts:228:3 - (ae-forgotten-export) The symbol "PathConfigType" needs to be exported by the entry point index.d.ts

View file

@ -72,6 +72,7 @@ export class Server {
private readonly metrics: MetricsService;
private readonly coreApp: CoreApp;
private pluginsInitialized?: boolean;
private coreStart?: InternalCoreStart;
constructor(
@ -156,6 +157,7 @@ export class Server {
};
const pluginsSetup = await this.plugins.setup(coreSetup);
this.pluginsInitialized = pluginsSetup.initialized;
const renderingSetup = await this.rendering.setup({
http: httpSetup,
@ -176,7 +178,9 @@ export class Server {
public async start() {
this.log.debug('starting server');
const savedObjectsStart = await this.savedObjects.start({});
const savedObjectsStart = await this.savedObjects.start({
pluginsInitialized: this.pluginsInitialized,
});
const capabilitiesStart = this.capabilities.start();
const uiSettingsStart = await this.uiSettings.start();
const elasticsearchStart = await this.elasticsearch.start();