Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into np/elasticsearch-…
Browse files Browse the repository at this point in the history
…types
  • Loading branch information
joshdover committed Oct 7, 2019
2 parents 58c12ac + c30a2f9 commit 46799bb
Show file tree
Hide file tree
Showing 40 changed files with 342 additions and 201 deletions.
51 changes: 51 additions & 0 deletions src/core/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* [How to](#how-to)
* [Configure plugin](#configure-plugin)
* [Mock new platform services in tests](#mock-new-platform-services-in-tests)
* [Provide Legacy Platform API to the New platform plugin](#provide-legacy-platform-api-to-the-new-platform-plugin)

Make no mistake, it is going to take a lot of work to move certain plugins to the new platform. Our target is to migrate the entire repo over to the new platform throughout 7.x and to remove the legacy plugin system no later than 8.0, and this is only possible if teams start on the effort now.

Expand Down Expand Up @@ -1196,3 +1197,53 @@ While our plan is to only provide first-class mocks for Jest tests, there are ma
For these tests, we are maintaining a separate set of mocks. Files with a `.karma_mock.{js|ts|tsx}` extension will be loaded _globally_ before karma tests are run.
It is important to note that this behavior is different from `jest.mock('ui/new_platform')`, which only mocks tests on an individual basis. If you encounter any failures in karma tests as a result of new platform migration efforts, you may need to add a `.karma_mock.js` file for the affected services, or add to the existing karma mock we are maintaining in `ui/new_platform`.
### Provide Legacy Platform API to the New platform plugin
#### On the server side
During migration, you can face a problem that not all API is available in the New platform yet. You can work around this by extending your
new platform plugin with Legacy API:
- create New platform plugin
- New platform plugin should expose a method `registerLegacyAPI` that allows passing API from the Legacy platform and store it in the NP plugin instance
```js
class MyPlugin {
public async setup(core){
return {
registerLegacyAPI: (legacyAPI) => (this.legacyAPI = legacyAPI)
}
}
}
```
- The legacy plugin provides API calling `registerLegacyAPI`
```js
new kibana.Plugin({
init(server){
const myPlugin = server.newPlatform.setup.plugins.myPlugin;
if (!myPlugin) {
throw new Error('myPlugin plugin is not available.');
}
myPlugin.registerLegacyAPI({ ... });
}
})
```
- The new platform plugin access stored Legacy platform API via `getLegacyAPI` getter. Getter function must have name indicating that’s API provided from the Legacy platform.
```js
class MyPlugin {
private getLegacyAPI(){
return this.legacyAPI;
}
public async setup(core){
const routeHandler = (context, req, req) => {
const legacyApi = this.getLegacyAPI();
// ...
}
return {
registerLegacyAPI: (legacyAPI) => (this.legacyAPI = legacyAPI)
}
}
}
```
#### On the client side
It's not currently possible to use a similar pattern on the client-side.
Because Legacy platform plugins heavily rely on global angular modules, which aren't available on the new platform.
So you can utilize the same approach for only *stateless Angular components*, as long as they are not consumed by a New Platform application. When New Platform applications are on the page, no legacy code is executed, so the `registerLegacyAPI` function would not be called.
1 change: 1 addition & 0 deletions src/core/public/core_system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const defaultCoreSystemParams = {

beforeEach(() => {
jest.clearAllMocks();
MockPluginsService.getOpaqueIds.mockReturnValue(new Map());
});

function createCoreSystem(params = {}) {
Expand Down
16 changes: 10 additions & 6 deletions src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export interface InternalCoreStart extends Omit<CoreStart, 'application'> {
export class CoreSystem {
private readonly fatalErrors: FatalErrorsService;
private readonly injectedMetadata: InjectedMetadataService;
private readonly legacyPlatform: LegacyPlatformService;
private readonly legacy: LegacyPlatformService;
private readonly notifications: NotificationsService;
private readonly http: HttpService;
private readonly savedObjects: SavedObjectsService;
Expand Down Expand Up @@ -134,7 +134,7 @@ export class CoreSystem {
this.context = new ContextService(this.coreContext);
this.plugins = new PluginsService(this.coreContext, injectedMetadata.uiPlugins);

this.legacyPlatform = new LegacyPlatformService({
this.legacy = new LegacyPlatformService({
requireLegacyFiles,
useLegacyTestHarness,
});
Expand All @@ -154,7 +154,11 @@ export class CoreSystem {
const notifications = this.notifications.setup({ uiSettings });

const pluginDependencies = this.plugins.getOpaqueIds();
const context = this.context.setup({ pluginDependencies });
const context = this.context.setup({
// We inject a fake "legacy plugin" with no dependencies so that legacy plugins can register context providers
// that will only be available to other legacy plugins and will not leak into New Platform plugins.
pluginDependencies: new Map([...pluginDependencies, [this.legacy.legacyId, []]]),
});
const application = this.application.setup({ context });

const core: InternalCoreSetup = {
Expand All @@ -170,7 +174,7 @@ export class CoreSystem {
// Services that do not expose contracts at setup
const plugins = await this.plugins.setup(core);

await this.legacyPlatform.setup({
await this.legacy.setup({
core,
plugins: mapToObject(plugins.contracts),
});
Expand Down Expand Up @@ -261,7 +265,7 @@ export class CoreSystem {
targetDomElement: coreUiTargetDomElement,
});

await this.legacyPlatform.start({
await this.legacy.start({
core,
plugins: mapToObject(plugins.contracts),
targetDomElement: rendering.legacyTargetDomElement,
Expand All @@ -278,7 +282,7 @@ export class CoreSystem {
}

public stop() {
this.legacyPlatform.stop();
this.legacy.stop();
this.plugins.stop();
this.notifications.stop();
this.http.stop();
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ interface BootstrapModule {
* setup either the app or browser tests.
*/
export class LegacyPlatformService {
/** Symbol to represent the legacy platform as a fake "plugin". Used by the ContextService */
public readonly legacyId = Symbol();
private bootstrapModule?: BootstrapModule;
private targetDomElement?: HTMLElement;

Expand Down
33 changes: 0 additions & 33 deletions src/core/server/http/integration_tests/core_services.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,39 +126,6 @@ describe('http service', () => {
.expect(200, { authorization: token, custom: 'custom-header' });
});

it('passes associated auth state to Legacy platform', async () => {
const user = { id: '42' };

const { http } = await root.setup();
const sessionStorageFactory = await http.createCookieSessionStorageFactory<StorageData>(
cookieOptions
);
http.registerAuth((req, res, toolkit) => {
if (req.headers.authorization) {
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: kbnServer.newPlatform.setup.core.http.auth.get,
});

const response = await kbnTestServer.request.get(root, legacyUrl).expect(200);
expect(response.body.state).toEqual(user);
expect(response.body.status).toEqual('authenticated');

expect(response.header['set-cookie']).toHaveLength(1);
});

it('attach security header to a successful response handled by Legacy platform', async () => {
const authResponseHeader = {
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
Expand Down
23 changes: 5 additions & 18 deletions src/core/server/legacy/legacy_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { DiscoveredPlugin, DiscoveredPluginInternal } from '../plugins';
import { PluginsServiceSetup, PluginsServiceStart } from '../plugins/plugins_service';
import { SavedObjectsServiceStart } from 'src/core/server/saved_objects/saved_objects_service';
import { KibanaMigrator } from '../saved_objects/migrations';
import { httpServiceMock } from '../http/http_service.mock';

const MockKbnServer: jest.Mock<KbnServer> = KbnServer as any;

Expand Down Expand Up @@ -86,6 +87,7 @@ beforeEach(() => {
context: contextServiceMock.createSetupContract(),
elasticsearch: { legacy: {} } as any,
http: {
...httpServiceMock.createSetupContract(),
auth: {
getAuthHeaders: () => undefined,
},
Expand Down Expand Up @@ -146,12 +148,7 @@ describe('once LegacyService is set up with connection info', () => {
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{
setupDeps,
startDeps,
handledConfigPaths: ['foo.bar'],
logger,
},
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);

Expand All @@ -176,12 +173,7 @@ describe('once LegacyService is set up with connection info', () => {
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{
setupDeps,
startDeps,
handledConfigPaths: ['foo.bar'],
logger,
},
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);

Expand Down Expand Up @@ -311,12 +303,7 @@ describe('once LegacyService is set up without connection info', () => {
expect(MockKbnServer).toHaveBeenCalledWith(
{ server: { autoListen: true } },
{ server: { autoListen: true } },
{
setupDeps,
startDeps,
handledConfigPaths: ['foo.bar'],
logger,
},
expect.any(Object),
{ disabledPluginSpecs: [], pluginSpecs: [], uiExports: [] }
);
});
Expand Down
43 changes: 40 additions & 3 deletions src/core/server/legacy/legacy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import { combineLatest, ConnectableObservable, EMPTY, Observable, Subscription } from 'rxjs';
import { first, map, publishReplay, tap } from 'rxjs/operators';
import { CoreService } from '../../types';
import { InternalCoreSetup, InternalCoreStart } from '../';
import { InternalCoreSetup, InternalCoreStart, CoreSetup, CoreStart } from '../';
import { SavedObjectsLegacyUiExports } from '../types';
import { Config } from '../config';
import { CoreContext } from '../core_context';
Expand Down Expand Up @@ -81,6 +81,8 @@ export interface LegacyServiceSetup {

/** @internal */
export class LegacyService implements CoreService<LegacyServiceSetup> {
/** Symbol to represent the legacy platform as a fake "plugin". Used by the ContextService */
public readonly legacyId = Symbol();
private readonly log: Logger;
private readonly devConfig$: Observable<DevConfig>;
private readonly httpConfig$: Observable<HttpConfig>;
Expand Down Expand Up @@ -226,15 +228,50 @@ export class LegacyService implements CoreService<LegacyServiceSetup> {
uiExports: SavedObjectsLegacyUiExports;
}
) {
const coreSetup: CoreSetup = {
context: setupDeps.core.context,
elasticsearch: {
adminClient$: setupDeps.core.elasticsearch.adminClient$,
dataClient$: setupDeps.core.elasticsearch.dataClient$,
createClient: setupDeps.core.elasticsearch.createClient,
},
http: {
createCookieSessionStorageFactory: setupDeps.core.http.createCookieSessionStorageFactory,
registerRouteHandlerContext: setupDeps.core.http.registerRouteHandlerContext.bind(
null,
this.legacyId
),
createRouter: () => setupDeps.core.http.createRouter('', this.legacyId),
registerOnPreAuth: setupDeps.core.http.registerOnPreAuth,
registerAuth: setupDeps.core.http.registerAuth,
registerOnPostAuth: setupDeps.core.http.registerOnPostAuth,
basePath: setupDeps.core.http.basePath,
isTlsEnabled: setupDeps.core.http.isTlsEnabled,
},
};
const coreStart: CoreStart = {};

// eslint-disable-next-line @typescript-eslint/no-var-requires
const KbnServer = require('../../../legacy/server/kbn_server');
const kbnServer: LegacyKbnServer = new KbnServer(
settings,
config,
{
handledConfigPaths: await this.coreContext.configService.getUsedPaths(),
setupDeps,
startDeps,
setupDeps: {
core: coreSetup,
plugins: setupDeps.plugins,
},
startDeps: {
core: coreStart,
plugins: startDeps.plugins,
},
__internals: {
hapiServer: setupDeps.core.http.server,
kibanaMigrator: startDeps.core.savedObjects.migrator,
uiPlugins: setupDeps.core.plugins.uiPlugins,
elasticsearch: setupDeps.core.elasticsearch,
},
logger: this.coreContext.logger,
},
legacyPlugins
Expand Down
1 change: 1 addition & 0 deletions src/core/server/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const logger = loggingServiceMock.create();

beforeEach(() => {
mockConfigService.atPath.mockReturnValue(new BehaviorSubject({ autoListen: true }));
mockPluginsService.discover.mockResolvedValue(new Map());
});

afterEach(() => {
Expand Down
6 changes: 5 additions & 1 deletion src/core/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ export class Server {

// Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph.
const pluginDependencies = await this.plugins.discover();
const contextServiceSetup = this.context.setup({ pluginDependencies });
const contextServiceSetup = this.context.setup({
// We inject a fake "legacy plugin" with no dependencies so that legacy plugins can register context providers
// that will only be available to other legacy plugins and will not leak into New Platform plugins.
pluginDependencies: new Map([...pluginDependencies, [this.legacy.legacyId, []]]),
});

const httpSetup = await this.http.setup({
context: contextServiceSetup,
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/core_plugins/console/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export default function(kibana: any) {
}

const config = server.config();
const legacyEsConfig = await server.newPlatform.setup.core.elasticsearch.legacy.config$
const legacyEsConfig = await server.newPlatform.__internals.elasticsearch.legacy.config$
.pipe(first())
.toPromise();
const proxyConfigCollection = new ProxyConfigCollection(options.proxyConfig);
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/core_plugins/elasticsearch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default function (kibana) {
// value from all observables here to be able to synchronously return and create
// cluster clients afterwards.
const [esConfig, adminCluster, dataCluster] = await combineLatest(
server.newPlatform.setup.core.elasticsearch.legacy.config$,
server.newPlatform.__internals.elasticsearch.legacy.config$,
server.newPlatform.setup.core.elasticsearch.adminClient$,
server.newPlatform.setup.core.elasticsearch.dataClient$
).pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ export default function () {
}
},
newPlatform: {
setup: {
core: {
elasticsearch: {
legacy: { config$: of({ shardTimeout: moment.duration(30000) }) }
}
__internals: {
elasticsearch: {
legacy: { config$: of({ shardTimeout: moment.duration(30000) }) }
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default new Datasource('es', {
});
}

const esShardTimeout = await tlConfig.server.newPlatform.setup.core.elasticsearch.legacy.config$.pipe(
const esShardTimeout = await tlConfig.server.newPlatform.__internals.elasticsearch.legacy.config$.pipe(
first(),
map(config => config.shardTimeout.asMilliseconds())
).toPromise();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ describe('getEsShardTimeout', () => {
const req = {
server: {
newPlatform: {
setup: {
core: {
elasticsearch: { legacy: { config$: of({ shardTimeout: moment.duration(12345) }) } },
},
__internals: {
elasticsearch: { legacy: { config$: of({ shardTimeout: moment.duration(12345) }) } },
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import { first, map } from 'rxjs/operators';

export async function getEsShardTimeout(req) {
return await req.server.newPlatform.setup.core.elasticsearch.legacy.config$
return await req.server.newPlatform.__internals.elasticsearch.legacy.config$
.pipe(
first(),
map(config => config.shardTimeout.asMilliseconds())
Expand Down
1 change: 0 additions & 1 deletion src/legacy/server/http/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { setupDefaultRouteProvider } from './setup_default_route_provider';
import { setupXsrf } from './xsrf';

export default async function (kbnServer, server, config) {
kbnServer.server = kbnServer.newPlatform.setup.core.http.server;
server = kbnServer.server;

setupBasePathProvider(kbnServer);
Expand Down
Loading

0 comments on commit 46799bb

Please sign in to comment.