Adds node.roles configuration & exposes via PluginInitializerContext (#135272)

This commit is contained in:
Luke Elmers 2022-06-28 16:22:43 -06:00 committed by GitHub
parent 3b6fc59e55
commit cf6ae210ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 1514 additions and 160 deletions

View file

@ -96,6 +96,9 @@ enabled:
- test/interactive_setup_functional/manual_configuration.config.ts
- test/interpreter_functional/config.ts
- test/new_visualize_flow/config.ts
- test/node_roles_functional/all.config.ts
- test/node_roles_functional/background_tasks.config.ts
- test/node_roles_functional/ui.config.ts
- test/plugin_functional/config.ts
- test/server_integration/http/platform/config.status.ts
- test/server_integration/http/platform/config.ts

View file

@ -311,6 +311,11 @@ Controls whether to enable the newsfeed
system for the {kib} UI notification center. Set to `false` to disable the
newsfeed system. *Default: `true`*
`node.roles`::
Indicates which roles to configure the {kib} process with, which will effectively
run {kib} in different modes. Valid options are `background_tasks` and `ui`,
or `*` to select all roles. *Default: `*`*
[[path-data]] `path.data`::
The path where {kib} stores persistent data
not saved in {es}. *Default: `data`*

View file

@ -181,6 +181,9 @@
"@kbn/core-logging-server": "link:bazel-bin/packages/core/logging/core-logging-server",
"@kbn/core-logging-server-internal": "link:bazel-bin/packages/core/logging/core-logging-server-internal",
"@kbn/core-logging-server-mocks": "link:bazel-bin/packages/core/logging/core-logging-server-mocks",
"@kbn/core-node-server": "link:bazel-bin/packages/core/node/core-node-server",
"@kbn/core-node-server-internal": "link:bazel-bin/packages/core/node/core-node-server-internal",
"@kbn/core-node-server-mocks": "link:bazel-bin/packages/core/node/core-node-server-mocks",
"@kbn/core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser",
"@kbn/core-theme-browser-internal": "link:bazel-bin/packages/core/theme/core-theme-browser-internal",
"@kbn/core-theme-browser-mocks": "link:bazel-bin/packages/core/theme/core-theme-browser-mocks",
@ -715,6 +718,9 @@
"@types/kbn__core-logging-server": "link:bazel-bin/packages/core/logging/core-logging-server/npm_module_types",
"@types/kbn__core-logging-server-internal": "link:bazel-bin/packages/core/logging/core-logging-server-internal/npm_module_types",
"@types/kbn__core-logging-server-mocks": "link:bazel-bin/packages/core/logging/core-logging-server-mocks/npm_module_types",
"@types/kbn__core-node-server": "link:bazel-bin/packages/core/node/core-node-server/npm_module_types",
"@types/kbn__core-node-server-internal": "link:bazel-bin/packages/core/node/core-node-server-internal/npm_module_types",
"@types/kbn__core-node-server-mocks": "link:bazel-bin/packages/core/node/core-node-server-mocks/npm_module_types",
"@types/kbn__core-public-internal-base": "link:bazel-bin/packages/core/public/internal-base/npm_module_types",
"@types/kbn__core-server-internal-base": "link:bazel-bin/packages/core/server/internal-base/npm_module_types",
"@types/kbn__core-theme-browser": "link:bazel-bin/packages/core/theme/core-theme-browser/npm_module_types",

View file

@ -50,6 +50,9 @@ filegroup(
"//packages/core/logging/core-logging-server-internal:build",
"//packages/core/logging/core-logging-server-mocks:build",
"//packages/core/logging/core-logging-server:build",
"//packages/core/node/core-node-server-internal:build",
"//packages/core/node/core-node-server-mocks:build",
"//packages/core/node/core-node-server:build",
"//packages/core/theme/core-theme-browser-internal:build",
"//packages/core/theme/core-theme-browser-mocks:build",
"//packages/core/theme/core-theme-browser:build",
@ -217,6 +220,9 @@ filegroup(
"//packages/core/logging/core-logging-server-internal:build_types",
"//packages/core/logging/core-logging-server-mocks:build_types",
"//packages/core/logging/core-logging-server:build_types",
"//packages/core/node/core-node-server-internal:build_types",
"//packages/core/node/core-node-server-mocks:build_types",
"//packages/core/node/core-node-server:build_types",
"//packages/core/theme/core-theme-browser-internal:build_types",
"//packages/core/theme/core-theme-browser-mocks:build_types",
"//packages/core/theme/core-theme-browser:build_types",

View file

@ -0,0 +1,104 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-node-server-internal"
PKG_REQUIRE_NAME = "@kbn/core-node-server-internal"
SOURCE_FILES = glob(
[
"src/**/*.ts",
],
exclude = [
"**/*.test.*",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
"@npm//lodash",
"@npm//rxjs",
"//packages/kbn-config-schema",
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
"@npm//@types/lodash",
"//packages/kbn-config:npm_module_types",
"//packages/kbn-config-schema:npm_module_types",
"//packages/kbn-logging:npm_module_types",
"//packages/core/base/core-base-server-internal:npm_module_types",
"//packages/core/node/core-node-server:npm_module_types",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = "src",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-node-server-internal
Contains internal code for Core's server-side `node` service.

View file

@ -0,0 +1,13 @@
/*
* 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/jest_node',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/node/core-node-server-internal'],
};

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/core-node-server-internal",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,12 @@
/*
* 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 { nodeConfig } from './node_config';
export { NodeService } from './node_service';
export type { InternalNodeServicePreboot } from './node_service';

View file

@ -0,0 +1,39 @@
/*
* 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 { schema } from '@kbn/config-schema';
import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal';
/** @internal */
export const NODE_CONFIG_PATH = 'node' as const;
/** @internal */
export const NODE_WILDCARD_CHAR = '*';
/** @internal */
export const NODE_ACCEPTED_ROLES = ['background_tasks', 'ui'];
/** @internal */
export interface NodeConfigType {
roles: string[];
}
const configSchema = schema.object({
roles: schema.oneOf(
[
schema.arrayOf(schema.oneOf([schema.literal('background_tasks'), schema.literal('ui')])),
schema.arrayOf(schema.literal(NODE_WILDCARD_CHAR), { minSize: 1, maxSize: 1 }),
],
{
defaultValue: [NODE_WILDCARD_CHAR],
}
),
});
export const nodeConfig: ServiceConfigDescriptor<NodeConfigType> = {
path: NODE_CONFIG_PATH,
schema: configSchema,
};

View file

@ -0,0 +1,107 @@
/*
* 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 { BehaviorSubject } from 'rxjs';
import type { CoreContext } from '@kbn/core-base-server-internal';
import { NodeService } from './node_service';
import { configServiceMock } from '@kbn/config-mocks';
import { mockCoreContext } from '@kbn/core-base-server-mocks';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
const getMockedConfigService = (nodeConfig: unknown) => {
const configService = configServiceMock.create();
configService.atPath.mockImplementation((path) => {
if (path === 'node') {
return new BehaviorSubject(nodeConfig);
}
return new BehaviorSubject({});
});
return configService;
};
describe('NodeService', () => {
let logger: ReturnType<typeof loggingSystemMock.create>;
let configService: ReturnType<typeof configServiceMock.create>;
let coreContext: CoreContext;
let service: NodeService;
beforeEach(async () => {
logger = loggingSystemMock.create();
});
afterEach(() => {
jest.clearAllMocks();
});
describe('#preboot()', () => {
it('returns default roles values when wildcard is provided', async () => {
configService = getMockedConfigService({ roles: ['*'] });
coreContext = mockCoreContext.create({ logger, configService });
service = new NodeService(coreContext);
const { roles } = await service.preboot();
expect(roles.backgroundTasks).toBe(true);
expect(roles.ui).toBe(true);
});
it('returns correct roles when node is configured to `background_tasks`', async () => {
configService = getMockedConfigService({ roles: ['background_tasks'] });
coreContext = mockCoreContext.create({ logger, configService });
service = new NodeService(coreContext);
const { roles } = await service.preboot();
expect(roles.backgroundTasks).toBe(true);
expect(roles.ui).toBe(false);
});
it('returns correct roles when node is configured to `ui`', async () => {
configService = getMockedConfigService({ roles: ['ui'] });
coreContext = mockCoreContext.create({ logger, configService });
service = new NodeService(coreContext);
const { roles } = await service.preboot();
expect(roles.backgroundTasks).toBe(false);
expect(roles.ui).toBe(true);
});
it('returns correct roles when node is configured to both `background_tasks` and `ui`', async () => {
configService = getMockedConfigService({ roles: ['background_tasks', 'ui'] });
coreContext = mockCoreContext.create({ logger, configService });
service = new NodeService(coreContext);
const { roles } = await service.preboot();
expect(roles.backgroundTasks).toBe(true);
expect(roles.ui).toBe(true);
});
it('logs the node roles', async () => {
const mockLogger = loggingSystemMock.createLogger();
logger.get.mockImplementation(() => mockLogger);
configService = getMockedConfigService({ roles: ['*'] });
coreContext = mockCoreContext.create({ logger, configService });
service = new NodeService(coreContext);
await service.preboot();
expect(logger.get).toHaveBeenCalledTimes(1);
expect(logger.get).toHaveBeenCalledWith('node');
expect(mockLogger.info).toHaveBeenCalledTimes(1);
expect(mockLogger.info.mock.calls[0][0]).toMatchInlineSnapshot(
`"Kibana process configured with roles: [background_tasks, ui]"`
);
});
});
});

View file

@ -0,0 +1,76 @@
/*
* 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 { firstValueFrom } from 'rxjs';
import { camelCase } from 'lodash';
import type { IConfigService } from '@kbn/config';
import type { CoreContext } from '@kbn/core-base-server-internal';
import type { NodeRoles } from '@kbn/core-node-server';
import type { Logger } from '@kbn/logging';
import {
NodeConfigType,
NODE_WILDCARD_CHAR,
NODE_ACCEPTED_ROLES,
NODE_CONFIG_PATH,
} from './node_config';
const DEFAULT_ROLES = NODE_ACCEPTED_ROLES;
const containsWildcard = (roles: string[]) => roles.includes(NODE_WILDCARD_CHAR);
/**
* @internal
*/
export interface InternalNodeServicePreboot {
/**
* Retrieve the Kibana instance uuid.
*/
roles: NodeRoles;
}
/** @internal */
export class NodeService {
private readonly configService: IConfigService;
private readonly log: Logger;
constructor(core: CoreContext) {
this.configService = core.configService;
this.log = core.logger.get('node');
}
public async preboot(): Promise<InternalNodeServicePreboot> {
const nodeRoles = await this.getNodeRoles();
this.log.info(`Kibana process configured with roles: [${nodeRoles.join(', ')}]`, {
service: {
// @ts-expect-error Field not available in ECS until 8.4
node: { roles: nodeRoles },
},
});
return {
roles: NODE_ACCEPTED_ROLES.reduce((acc, curr) => {
return { ...acc, [camelCase(curr)]: nodeRoles.includes(curr) };
}, {} as NodeRoles),
};
}
public stop() {
// nothing to do here yet
}
private async getNodeRoles(): Promise<string[]> {
const { roles } = await firstValueFrom(
this.configService.atPath<NodeConfigType>(NODE_CONFIG_PATH)
);
if (containsWildcard(roles)) {
return DEFAULT_ROLES;
}
return roles;
}
}

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": "src",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"src/**/*"
]
}

View file

@ -0,0 +1,97 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-node-server-mocks"
PKG_REQUIRE_NAME = "@kbn/core-node-server-mocks"
SOURCE_FILES = glob(
[
"src/**/*.ts",
],
exclude = [
"**/*.test.*",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
'//packages/core/node/core-node-server-internal'
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
'//packages/core/node/core-node-server-internal:npm_module_types'
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = "src",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-node-server-mocks
Contains mocks for Core's server-side `node` service.

View file

@ -0,0 +1,13 @@
/*
* 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/jest_node',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/node/core-node-server-mocks'],
};

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/core-node-server-mocks",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,9 @@
/*
* 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 { nodeServiceMock } from './node_service.mock';

View file

@ -0,0 +1,35 @@
/*
* 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 type { PublicMethodsOf } from '@kbn/utility-types';
import type { NodeService, InternalNodeServicePreboot } from '@kbn/core-node-server-internal';
const createInternalPrebootContractMock = () => {
const prebootContract: jest.Mocked<InternalNodeServicePreboot> = {
roles: {
backgroundTasks: true,
ui: true,
},
};
return prebootContract;
};
type NodeServiceContract = PublicMethodsOf<NodeService>;
const createMock = () => {
const mocked: jest.Mocked<NodeServiceContract> = {
preboot: jest.fn(),
stop: jest.fn(),
};
mocked.preboot.mockResolvedValue(createInternalPrebootContractMock());
return mocked;
};
export const nodeServiceMock = {
create: createMock,
createInternalPrebootContract: createInternalPrebootContractMock,
};

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": "src",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"src/**/*"
]
}

View file

@ -0,0 +1,95 @@
load("@npm//@bazel/typescript:index.bzl", "ts_config")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
PKG_DIRNAME = "core-node-server"
PKG_REQUIRE_NAME = "@kbn/core-node-server"
SOURCE_FILES = glob(
[
"src/**/*.ts",
],
exclude = [
"**/*.test.*",
],
)
SRCS = SOURCE_FILES
filegroup(
name = "srcs",
srcs = SRCS,
)
NPM_MODULE_EXTRA_FILES = [
"package.json",
]
RUNTIME_DEPS = [
]
TYPES_DEPS = [
"@npm//@types/node",
"@npm//@types/jest",
]
jsts_transpiler(
name = "target_node",
srcs = SRCS,
build_pkg_name = package_name(),
)
ts_config(
name = "tsconfig",
src = "tsconfig.json",
deps = [
"//:tsconfig.base.json",
"//:tsconfig.bazel.json",
],
)
ts_project(
name = "tsc_types",
args = ['--pretty'],
srcs = SRCS,
deps = TYPES_DEPS,
declaration = True,
emit_declaration_only = True,
out_dir = "target_types",
root_dir = "src",
tsconfig = ":tsconfig",
)
js_library(
name = PKG_DIRNAME,
srcs = NPM_MODULE_EXTRA_FILES,
deps = RUNTIME_DEPS + [":target_node"],
package_name = PKG_REQUIRE_NAME,
visibility = ["//visibility:public"],
)
pkg_npm(
name = "npm_module",
deps = [":" + PKG_DIRNAME],
)
filegroup(
name = "build",
srcs = [":npm_module"],
visibility = ["//visibility:public"],
)
pkg_npm_types(
name = "npm_module_types",
srcs = SRCS,
deps = [":tsc_types"],
package_name = PKG_REQUIRE_NAME,
tsconfig = ":tsconfig",
visibility = ["//visibility:public"],
)
filegroup(
name = "build_types",
srcs = [":npm_module_types"],
visibility = ["//visibility:public"],
)

View file

@ -0,0 +1,3 @@
# @kbn/core-node-server
Contains public types for Core's server-side `node` service.

View file

@ -0,0 +1,13 @@
/*
* 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/jest_node',
rootDir: '../../../..',
roots: ['<rootDir>/packages/core/node/core-node-server'],
};

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/core-node-server",
"private": true,
"version": "1.0.0",
"main": "./target_node/index.js",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,9 @@
/*
* 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 type { NodeInfo, NodeRoles } from './types';

View file

@ -0,0 +1,40 @@
/*
* 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.
*/
/**
* Contains information about how this Kibana process has been configured.
*
* @public
*/
export interface NodeInfo {
/** A list of roles this node has been configured with. */
roles: NodeRoles;
}
/**
* The Kibana process can be run in dedicated "modes" via `node.roles`.
* This configuration is then exposed to plugins via `NodeRoles`,
* which is available on the `PluginInitializerContext`.
*
* The node roles can be used by plugins to adjust their behavior based
* on the way the Kibana process has been configured.
*
* @public
*/
export interface NodeRoles {
/**
* The backgroundTasks role includes operations which don't involve
* responding to incoming http traffic from the UI.
*/
backgroundTasks: boolean;
/**
* The ui role covers any operations that need to occur in order
* to handle http traffic from the browser.
*/
ui: boolean;
}

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../tsconfig.bazel.json",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "target_types",
"rootDir": "src",
"stripInternal": false,
"types": [
"jest",
"node"
]
},
"include": [
"src/**/*"
]
}

View file

@ -262,6 +262,8 @@ export type {
LogLevel,
} from '@kbn/logging';
export type { NodeInfo, NodeRoles } from '@kbn/core-node-server';
export { PluginType } from '@kbn/core-base-common';
export type {

View file

@ -14,6 +14,7 @@ import type { MockedKeys } from '@kbn/utility-types-jest';
import { docLinksServiceMock } from '@kbn/core-doc-links-server-mocks';
import { loggingSystemMock, loggingServiceMock } from '@kbn/core-logging-server-mocks';
import { analyticsServiceMock } from '@kbn/core-analytics-server-mocks';
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
import type {
PluginInitializerContext,
CoreSetup,
@ -124,6 +125,7 @@ function pluginInitializerContextMock<T>(config: T = {} as T) {
configs: ['/some/path/to/config/kibana.yml'],
},
config: pluginInitializerContextConfigMock<T>(config),
node: nodeServiceMock.createInternalPrebootContract(),
};
return mock;

View file

@ -18,6 +18,7 @@ import { map, toArray } from 'rxjs/operators';
import { resolve } from 'path';
import { ConfigService, Env } from '@kbn/config';
import type { CoreContext } from '@kbn/core-base-server-internal';
import type { NodeInfo } from '@kbn/core-node-server';
import { PluginsConfig, PluginsConfigType, config } from '../plugins_config';
import type { InstanceInfo } from '../plugin_context';
import { discover } from './plugins_discovery';
@ -128,6 +129,7 @@ const manifestPath = (...pluginPath: string[]) =>
describe('plugins discovery system', () => {
let logger: ReturnType<typeof loggingSystemMock.create>;
let instanceInfo: InstanceInfo;
let nodeInfo: NodeInfo;
let env: Env;
let configService: ConfigService;
let pluginConfig: PluginsConfigType;
@ -142,6 +144,13 @@ describe('plugins discovery system', () => {
uuid: 'instance-uuid',
};
nodeInfo = {
roles: {
backgroundTasks: true,
ui: true,
},
};
env = Env.createDefault(
REPO_ROOT,
getEnvOptions({
@ -180,7 +189,12 @@ describe('plugins discovery system', () => {
});
it('discovers plugins in the search locations', async () => {
const { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo);
const { plugin$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -202,11 +216,12 @@ describe('plugins discovery system', () => {
});
it('return errors when the manifest is invalid or incompatible', async () => {
const { plugin$, error$ } = discover(
new PluginsConfig(pluginConfig, env),
const { plugin$, error$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo
);
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -268,11 +283,12 @@ describe('plugins discovery system', () => {
});
it('return errors when the plugin search path is not accessible', async () => {
const { plugin$, error$ } = discover(
new PluginsConfig(pluginConfig, env),
const { plugin$, error$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo
);
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -307,11 +323,12 @@ describe('plugins discovery system', () => {
});
it('return an error when the manifest file is not accessible', async () => {
const { plugin$, error$ } = discover(
new PluginsConfig(pluginConfig, env),
const { plugin$, error$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo
);
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -342,11 +359,12 @@ describe('plugins discovery system', () => {
});
it('discovers plugins in nested directories', async () => {
const { plugin$, error$ } = discover(
new PluginsConfig(pluginConfig, env),
const { plugin$, error$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo
);
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -386,7 +404,12 @@ describe('plugins discovery system', () => {
});
it('does not discover plugins nested inside another plugin', async () => {
const { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo);
const { plugin$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -405,7 +428,12 @@ describe('plugins discovery system', () => {
});
it('stops scanning when reaching `maxDepth`', async () => {
const { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo);
const { plugin$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
});
mockFs(
{
@ -430,7 +458,12 @@ describe('plugins discovery system', () => {
});
it('works with symlinks', async () => {
const { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo);
const { plugin$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
});
const pluginFolder = resolve(KIBANA_ROOT, '..', 'ext-plugins');
@ -465,16 +498,17 @@ describe('plugins discovery system', () => {
})
);
discover(
new PluginsConfig({ ...pluginConfig, paths: [extraPluginTestPath] }, env),
{
discover({
config: new PluginsConfig({ ...pluginConfig, paths: [extraPluginTestPath] }, env),
coreContext: {
coreId: Symbol(),
configService,
env,
logger,
},
instanceInfo
);
instanceInfo,
nodeInfo,
});
expect(loggingSystemMock.collect(logger).warn).toEqual([
[
@ -493,16 +527,17 @@ describe('plugins discovery system', () => {
})
);
discover(
new PluginsConfig({ ...pluginConfig, paths: [extraPluginTestPath] }, env),
{
discover({
config: new PluginsConfig({ ...pluginConfig, paths: [extraPluginTestPath] }, env),
coreContext: {
coreId: Symbol(),
configService,
env,
logger,
},
instanceInfo
);
instanceInfo,
nodeInfo,
});
expect(loggingSystemMock.collect(logger).warn).toEqual([]);
});
@ -530,7 +565,12 @@ describe('plugins discovery system', () => {
])
);
let { plugin$ } = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo);
let { plugin$ } = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
});
expect(scanPluginSearchPathsMock).toHaveBeenCalledTimes(1);
let plugins = await firstValueFrom(plugin$.pipe(toArray()));
@ -549,7 +589,12 @@ describe('plugins discovery system', () => {
])
);
plugin$ = discover(new PluginsConfig(pluginConfig, env), coreContext, instanceInfo).plugin$;
plugin$ = discover({
config: new PluginsConfig(pluginConfig, env),
coreContext,
instanceInfo,
nodeInfo,
}).plugin$;
expect(scanPluginSearchPathsMock).toHaveBeenCalledTimes(2);
plugins = await firstValueFrom(plugin$.pipe(toArray()));

View file

@ -10,6 +10,7 @@ import { from, merge } from 'rxjs';
import { catchError, filter, map, mergeMap, concatMap, shareReplay, toArray } from 'rxjs/operators';
import { Logger } from '@kbn/logging';
import type { CoreContext } from '@kbn/core-base-server-internal';
import type { NodeInfo } from '@kbn/core-node-server';
import { PluginWrapper } from '../plugin';
import { createPluginInitializerContext, InstanceInfo } from '../plugin_context';
import { PluginsConfig } from '../plugins_config';
@ -27,11 +28,17 @@ import { scanPluginSearchPaths } from './scan_plugin_search_paths';
* @param coreContext Kibana core values.
* @internal
*/
export function discover(
config: PluginsConfig,
coreContext: CoreContext,
instanceInfo: InstanceInfo
) {
export function discover({
config,
coreContext,
instanceInfo,
nodeInfo,
}: {
config: PluginsConfig;
coreContext: CoreContext;
instanceInfo: InstanceInfo;
nodeInfo: NodeInfo;
}) {
const log = coreContext.logger.get('plugins-discovery');
log.debug('Discovering plugins...');
@ -55,7 +62,7 @@ export function discover(
}),
concatMap((pluginPathOrError) => {
return typeof pluginPathOrError === 'string'
? createPlugin$(pluginPathOrError, log, coreContext, instanceInfo)
? createPlugin$(pluginPathOrError, log, coreContext, instanceInfo, nodeInfo)
: [pluginPathOrError];
}),
shareReplay()
@ -78,12 +85,15 @@ export function discover(
* @param path Path to the plugin directory where manifest should be loaded from.
* @param log Plugin discovery logger instance.
* @param coreContext Kibana core context.
* @param instanceInfo Info about the instance running Kibana, including uuid.
* @param nodeRoles Roles this process has been configured with.
*/
function createPlugin$(
path: string,
log: Logger,
coreContext: CoreContext,
instanceInfo: InstanceInfo
instanceInfo: InstanceInfo,
nodeInfo: NodeInfo
) {
return from(parseManifest(path, coreContext.env.packageInfo)).pipe(
map((manifest) => {
@ -93,12 +103,13 @@ function createPlugin$(
path,
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
}),
catchError((err) => [err])

View file

@ -15,6 +15,7 @@ import { join } from 'path';
import { ConfigPath, ConfigService, Env } from '@kbn/config';
import { getEnvOptions, rawConfigServiceMock } from '@kbn/config-mocks';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
import { PluginsService } from '../plugins_service';
import { BehaviorSubject, from } from 'rxjs';
import { config } from '../plugins_config';
@ -26,6 +27,7 @@ import { PluginWrapper } from '../plugin';
describe('PluginsService', () => {
const logger = loggingSystemMock.create();
const environmentPreboot = environmentServiceMock.createPrebootContract();
const nodePreboot = nodeServiceMock.createInternalPrebootContract();
let pluginsService: PluginsService;
const createPlugin = (
@ -156,7 +158,7 @@ describe('PluginsService', () => {
}
);
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
const prebootDeps = coreMock.createInternalPreboot();
await pluginsService.preboot(prebootDeps);

View file

@ -15,6 +15,8 @@ import { Env } from '@kbn/config';
import { configServiceMock, getEnvOptions } from '@kbn/config-mocks';
import type { CoreContext } from '@kbn/core-base-server-internal';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import type { NodeInfo } from '@kbn/core-node-server';
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
import { coreMock } from '../mocks';
import { PluginWrapper } from './plugin';
@ -68,6 +70,7 @@ let coreId: symbol;
let env: Env;
let coreContext: CoreContext;
let instanceInfo: InstanceInfo;
let nodeInfo: NodeInfo;
const setupDeps = coreMock.createInternalSetup();
@ -77,6 +80,7 @@ beforeEach(() => {
instanceInfo = {
uuid: 'instance-uuid',
};
nodeInfo = nodeServiceMock.createInternalPrebootContract();
coreContext = { coreId, env, logger, configService: configService as any };
});
@ -92,12 +96,13 @@ test('`constructor` correctly initializes plugin instance', () => {
path: 'some-plugin-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(plugin.name).toBe('some-plugin-id');
@ -116,12 +121,13 @@ describe('`constructor` correctly sets non-external source', () => {
path,
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
}
@ -153,12 +159,13 @@ test('`setup` fails if `plugin` initializer is not exported', () => {
path: 'plugin-without-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(() =>
@ -175,12 +182,13 @@ test('`setup` fails if plugin initializer is not a function', () => {
path: 'plugin-with-wrong-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(() =>
@ -197,12 +205,13 @@ test('`setup` fails if initializer does not return object', () => {
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
mockPluginInitializer.mockReturnValue(null);
@ -221,12 +230,13 @@ test('`setup` fails if object returned from initializer does not define `setup`
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const mockPluginInstance = { run: jest.fn() };
@ -242,12 +252,13 @@ test('`setup` fails if object returned from initializer does not define `setup`
test('`setup` initializes plugin and calls appropriate lifecycle hook', async () => {
const manifest = createPluginManifest();
const opaqueId = Symbol();
const initializerContext = createPluginInitializerContext(
const initializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
);
instanceInfo,
nodeInfo,
});
const plugin = new PluginWrapper({
path: 'plugin-with-initializer-path',
manifest,
@ -276,12 +287,13 @@ test('`start` fails if setup is not called first', () => {
path: 'some-plugin-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(() => plugin.start({} as any, {} as any)).toThrowErrorMatchingInlineSnapshot(
@ -296,12 +308,13 @@ test('`start` fails invoked for the `preboot` plugin', async () => {
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const mockPluginInstance = { setup: jest.fn() };
@ -321,12 +334,13 @@ test('`start` calls plugin.start with context and dependencies', async () => {
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const context = { any: 'thing' } as any;
const deps = { otherDep: 'value' };
@ -355,12 +369,13 @@ test("`start` resolves `startDependencies` Promise after plugin's start", async
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const startContext = { any: 'thing' } as any;
const pluginDeps = { someDep: 'value' };
@ -399,12 +414,13 @@ test('`stop` fails if plugin is not set up', async () => {
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const mockPluginInstance = { setup: jest.fn(), stop: jest.fn() };
@ -423,12 +439,13 @@ test('`stop` does nothing if plugin does not define `stop` function', async () =
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
mockPluginInitializer.mockReturnValue({ setup: jest.fn() });
@ -444,12 +461,13 @@ test('`stop` calls `stop` defined by the plugin instance', async () => {
path: 'plugin-with-initializer-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
const mockPluginInstance = { setup: jest.fn(), stop: jest.fn() };
@ -479,12 +497,13 @@ describe('#getConfigSchema()', () => {
path: 'plugin-with-schema',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(plugin.getConfigDescriptor()).toBe(configDescriptor);
@ -498,12 +517,13 @@ describe('#getConfigSchema()', () => {
path: 'plugin-with-no-definition',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(plugin.getConfigDescriptor()).toBe(null);
});
@ -515,12 +535,13 @@ describe('#getConfigSchema()', () => {
path: 'plugin-with-no-definition',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(plugin.getConfigDescriptor()).toBe(null);
});
@ -543,12 +564,13 @@ describe('#getConfigSchema()', () => {
path: 'plugin-invalid-schema',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
),
instanceInfo,
nodeInfo,
}),
});
expect(() => plugin.getConfigDescriptor()).toThrowErrorMatchingInlineSnapshot(
`"Configuration schema expected to be an instance of Type"`

View file

@ -13,6 +13,8 @@ import { fromRoot } from '@kbn/utils';
import { rawConfigServiceMock, getEnvOptions, configServiceMock } from '@kbn/config-mocks';
import type { CoreContext } from '@kbn/core-base-server-internal';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import type { NodeInfo } from '@kbn/core-node-server';
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
import {
createPluginInitializerContext,
createPluginPrebootSetupContext,
@ -54,6 +56,7 @@ describe('createPluginInitializerContext', () => {
let coreContext: CoreContext;
let server: Server;
let instanceInfo: InstanceInfo;
let nodeInfo: NodeInfo;
beforeEach(async () => {
logger = loggingSystemMock.create();
@ -62,6 +65,7 @@ describe('createPluginInitializerContext', () => {
instanceInfo = {
uuid: 'instance-uuid',
};
nodeInfo = nodeServiceMock.createInternalPrebootContract();
env = Env.createDefault(REPO_ROOT, getEnvOptions());
const config$ = rawConfigServiceMock.create({ rawConfig: {} });
server = new Server(config$, env, logger);
@ -96,12 +100,13 @@ describe('createPluginInitializerContext', () => {
configPath: 'plugin',
});
const pluginInitializerContext = createPluginInitializerContext(
const pluginInitializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
);
instanceInfo,
nodeInfo,
});
expect(pluginInitializerContext.config.get()).toEqual({
foo: 'bar',
@ -111,12 +116,13 @@ describe('createPluginInitializerContext', () => {
it('config.globalConfig$ should be an observable for the global config', async () => {
const manifest = createPluginManifest();
const pluginInitializerContext = createPluginInitializerContext(
const pluginInitializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
);
instanceInfo,
nodeInfo,
});
expect(pluginInitializerContext.config.legacy.globalConfig$).toBeDefined();
@ -141,12 +147,13 @@ describe('createPluginInitializerContext', () => {
instanceInfo = {
uuid: 'kibana-uuid',
};
const pluginInitializerContext = createPluginInitializerContext(
const pluginInitializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo
);
instanceInfo,
nodeInfo,
});
expect(pluginInitializerContext.env.instanceUuid).toBe('kibana-uuid');
});
@ -160,23 +167,39 @@ describe('createPluginInitializerContext', () => {
})
),
};
const pluginInitializerContext = createPluginInitializerContext(
const pluginInitializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
createPluginManifest(),
instanceInfo
);
manifest: createPluginManifest(),
instanceInfo,
nodeInfo,
});
expect(pluginInitializerContext.env.configs).toEqual([
'/home/kibana/config/kibana.yml',
'/home/kibana/config/kibana.dev.yml',
]);
});
});
describe('context.node', () => {
it('should expose the correct node roles', () => {
const pluginInitializerContext = createPluginInitializerContext({
coreContext,
opaqueId,
manifest: createPluginManifest(),
instanceInfo,
nodeInfo: { roles: { backgroundTasks: false, ui: true } },
});
expect(pluginInitializerContext.node.roles.backgroundTasks).toBe(false);
expect(pluginInitializerContext.node.roles.ui).toBe(true);
});
});
});
describe('createPluginPrebootSetupContext', () => {
let coreContext: CoreContext;
let opaqueId: symbol;
let nodeInfo: NodeInfo;
beforeEach(async () => {
opaqueId = Symbol();
@ -186,6 +209,7 @@ describe('createPluginPrebootSetupContext', () => {
logger: loggingSystemMock.create(),
configService: configServiceMock.create(),
};
nodeInfo = nodeServiceMock.createInternalPrebootContract();
});
it('`holdSetupUntilResolved` captures plugin.name', () => {
@ -194,8 +218,14 @@ describe('createPluginPrebootSetupContext', () => {
path: 'some-path',
manifest,
opaqueId,
initializerContext: createPluginInitializerContext(coreContext, opaqueId, manifest, {
uuid: 'instance-uuid',
initializerContext: createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo: {
uuid: 'instance-uuid',
},
nodeInfo,
}),
});

View file

@ -9,6 +9,7 @@
import { shareReplay } from 'rxjs/operators';
import type { CoreContext } from '@kbn/core-base-server-internal';
import type { PluginOpaqueId } from '@kbn/core-base-common';
import type { NodeInfo } from '@kbn/core-node-server';
import type { RequestHandlerContext } from '..';
import { PluginWrapper } from './plugin';
import {
@ -21,6 +22,7 @@ import { IRouter, RequestHandlerContextProvider } from '../http';
import { getGlobalConfig, getGlobalConfig$ } from './legacy_config';
import { CorePreboot, CoreSetup, CoreStart } from '..';
/** @internal */
export interface InstanceInfo {
uuid: string;
}
@ -35,15 +37,26 @@ export interface InstanceInfo {
* We should aim to be restrictive and specific in the APIs that we expose.
*
* @param coreContext Kibana core context
* @param pluginManifest The manifest of the plugin we're building these values for.
* @param opaqueId The opaque id created for this particular plugin.
* @param manifest The manifest of the plugin we're building these values for.
* @param instanceInfo Info about the instance Kibana is running on.
* @param nodeInfo Info about how the Kibana process has been configured.
*
* @internal
*/
export function createPluginInitializerContext(
coreContext: CoreContext,
opaqueId: PluginOpaqueId,
pluginManifest: PluginManifest,
instanceInfo: InstanceInfo
): PluginInitializerContext {
export function createPluginInitializerContext({
coreContext,
opaqueId,
manifest,
instanceInfo,
nodeInfo,
}: {
coreContext: CoreContext;
opaqueId: PluginOpaqueId;
manifest: PluginManifest;
instanceInfo: InstanceInfo;
nodeInfo: NodeInfo;
}): PluginInitializerContext {
return {
opaqueId,
@ -57,12 +70,23 @@ export function createPluginInitializerContext(
configs: coreContext.env.configs,
},
/**
* Access the configuration for this particular Kibana node.
* Can be used to determine which `roles` the current process was started with.
*/
node: {
roles: {
backgroundTasks: nodeInfo.roles.backgroundTasks,
ui: nodeInfo.roles.ui,
},
},
/**
* Plugin-scoped logger
*/
logger: {
get(...contextParts) {
return coreContext.logger.get('plugins', pluginManifest.id, ...contextParts);
return coreContext.logger.get('plugins', manifest.id, ...contextParts);
},
},
@ -80,10 +104,10 @@ export function createPluginInitializerContext(
* manifest.
*/
create<T>() {
return coreContext.configService.atPath<T>(pluginManifest.configPath).pipe(shareReplay(1));
return coreContext.configService.atPath<T>(manifest.configPath).pipe(shareReplay(1));
},
get<T>() {
return coreContext.configService.atPathSync<T>(pluginManifest.configPath);
return coreContext.configService.atPathSync<T>(manifest.configPath);
},
},
};

View file

@ -17,6 +17,7 @@ import { ConfigPath, ConfigService, Env } from '@kbn/config';
import { rawConfigServiceMock, getEnvOptions } from '@kbn/config-mocks';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
import { coreMock } from '../mocks';
import { environmentServiceMock } from '../environment/environment_service.mock';
import { PluginDiscoveryError } from './discovery';
@ -37,6 +38,7 @@ let env: Env;
let prebootMockPluginSystem: jest.Mocked<PluginsSystem<PluginType.preboot>>;
let standardMockPluginSystem: jest.Mocked<PluginsSystem<PluginType.standard>>;
let environmentPreboot: ReturnType<typeof environmentServiceMock.createPrebootContract>;
let nodePreboot: ReturnType<typeof nodeServiceMock.createInternalPrebootContract>;
const prebootDeps = coreMock.createInternalPreboot();
const setupDeps = coreMock.createInternalSetup();
@ -140,6 +142,7 @@ async function testSetup() {
standardMockPluginSystem.getPlugins.mockReturnValue([]);
environmentPreboot = environmentServiceMock.createPrebootContract();
nodePreboot = nodeServiceMock.createInternalPrebootContract();
}
afterEach(() => {
@ -158,8 +161,8 @@ describe('PluginsService', () => {
plugin$: from([]),
});
await expect(pluginsService.discover({ environment: environmentPreboot })).rejects
.toMatchInlineSnapshot(`
await expect(pluginsService.discover({ environment: environmentPreboot, node: nodePreboot }))
.rejects.toMatchInlineSnapshot(`
[Error: Failed to initialize plugins:
Invalid JSON (invalid-manifest, path-1)]
`);
@ -180,8 +183,8 @@ describe('PluginsService', () => {
plugin$: from([]),
});
await expect(pluginsService.discover({ environment: environmentPreboot })).rejects
.toMatchInlineSnapshot(`
await expect(pluginsService.discover({ environment: environmentPreboot, node: nodePreboot }))
.rejects.toMatchInlineSnapshot(`
[Error: Failed to initialize plugins:
Incompatible version (incompatible-version, path-3)]
`);
@ -216,7 +219,7 @@ describe('PluginsService', () => {
});
await expect(
pluginsService.discover({ environment: environmentPreboot })
pluginsService.discover({ environment: environmentPreboot, node: nodePreboot })
).rejects.toMatchInlineSnapshot(
`[Error: Plugin with id "conflicting-id" is already registered!]`
);
@ -250,7 +253,7 @@ describe('PluginsService', () => {
});
await expect(
pluginsService.discover({ environment: environmentPreboot })
pluginsService.discover({ environment: environmentPreboot, node: nodePreboot })
).rejects.toMatchInlineSnapshot(
`[Error: Plugin with id "conflicting-id" is already registered!]`
);
@ -288,16 +291,18 @@ describe('PluginsService', () => {
}
async function expectError() {
await expect(pluginsService.discover({ environment: environmentPreboot })).rejects.toThrow(
await expect(
pluginsService.discover({ environment: environmentPreboot, node: nodePreboot })
).rejects.toThrow(
`X-Pack plugin or bundle with id "xPackPlugin" is required by OSS plugin "sourcePlugin", which is prohibited. Consider making this an optional dependency instead.`
);
expect(standardMockPluginSystem.addPlugin).not.toHaveBeenCalled();
}
async function expectSuccess() {
await expect(pluginsService.discover({ environment: environmentPreboot })).resolves.toEqual(
expect.anything()
);
await expect(
pluginsService.discover({ environment: environmentPreboot, node: nodePreboot })
).resolves.toEqual(expect.anything());
expect(standardMockPluginSystem.addPlugin).toHaveBeenCalled();
}
@ -433,7 +438,7 @@ describe('PluginsService', () => {
]),
});
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
const setup = await pluginsService.setup(setupDeps);
@ -521,6 +526,7 @@ describe('PluginsService', () => {
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(mockDiscover).toHaveBeenCalledTimes(1);
@ -562,6 +568,7 @@ describe('PluginsService', () => {
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(preboot.pluginTree).toBeUndefined();
expect(standard.pluginTree).toBeUndefined();
@ -630,6 +637,7 @@ describe('PluginsService', () => {
const { standard, preboot } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(mockDiscover).toHaveBeenCalledTimes(1);
@ -696,7 +704,7 @@ describe('PluginsService', () => {
plugin$: from([...prebootPlugins, ...standardPlugins]),
});
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
expect(prebootMockPluginSystem.addPlugin).toHaveBeenCalledTimes(2);
for (const plugin of prebootPlugins) {
expect(prebootMockPluginSystem.addPlugin).toHaveBeenCalledWith(plugin);
@ -708,8 +716,8 @@ describe('PluginsService', () => {
}
expect(mockDiscover).toHaveBeenCalledTimes(1);
expect(mockDiscover).toHaveBeenCalledWith(
{
expect(mockDiscover).toHaveBeenCalledWith({
config: {
additionalPluginPaths: [],
initialize: true,
pluginSearchPaths: [
@ -719,9 +727,10 @@ describe('PluginsService', () => {
resolve(process.cwd(), '..', 'kibana-extra'),
],
},
{ coreId, env, logger, configService },
{ uuid: 'uuid' }
);
coreContext: { coreId, env, logger, configService },
instanceInfo: { uuid: 'uuid' },
nodeInfo: { roles: { backgroundTasks: true, ui: true } },
});
const logs = loggingSystemMock.collect(logger);
expect(logs.info).toHaveLength(0);
@ -756,7 +765,7 @@ describe('PluginsService', () => {
}),
]),
});
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
expect(configService.setSchema).toBeCalledWith('path-preboot', configSchema);
expect(configService.setSchema).toBeCalledWith('path-standard', configSchema);
});
@ -794,7 +803,7 @@ describe('PluginsService', () => {
}),
]),
});
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
expect(configService.addDeprecationProvider).toBeCalledWith(
'config-path-preboot',
prebootDeprecationProvider
@ -837,6 +846,7 @@ describe('PluginsService', () => {
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(preboot.pluginPaths).toEqual(['/plugin-A-path-preboot', '/plugin-B-path-preboot']);
@ -926,7 +936,7 @@ describe('PluginsService', () => {
]),
});
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
// eslint-disable-next-line dot-notation
expect(pluginsService['pluginConfigUsageDescriptors']).toMatchInlineSnapshot(`
@ -1008,6 +1018,7 @@ describe('PluginsService', () => {
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
const prebootUIConfig$ = preboot.uiPlugins.browserConfigs.get('plugin-with-expose-preboot')!;
@ -1062,6 +1073,7 @@ describe('PluginsService', () => {
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(preboot.uiPlugins.browserConfigs.size).toBe(0);
expect(standard.uiPlugins.browserConfigs.size).toBe(0);
@ -1125,6 +1137,7 @@ describe('PluginsService', () => {
await expect(
pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
})
).resolves.not.toThrow(); // If the rename is not applied, it'll fail
});
@ -1185,6 +1198,7 @@ describe('PluginsService', () => {
});
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect(preboot.uiPlugins.internal).toMatchInlineSnapshot(`
Map {
@ -1228,6 +1242,7 @@ describe('PluginsService', () => {
});
const { preboot, standard } = await pluginsService.discover({
environment: environmentPreboot,
node: nodePreboot,
});
expect([...preboot.uiPlugins.internal.keys()].sort()).toMatchInlineSnapshot(`
Array [
@ -1245,7 +1260,7 @@ describe('PluginsService', () => {
it('#preboot does initialize `preboot` plugins if plugins.initialize is true', async () => {
config$.next({ plugins: { initialize: true } });
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
expect(prebootMockPluginSystem.setupPlugins).toHaveBeenCalledTimes(1);
@ -1255,7 +1270,7 @@ describe('PluginsService', () => {
it('#preboot does not initialize `preboot` plugins if plugins.initialize is false', async () => {
config$.next({ plugins: { initialize: false } });
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
expect(prebootMockPluginSystem.setupPlugins).not.toHaveBeenCalled();
@ -1264,7 +1279,7 @@ describe('PluginsService', () => {
it('#setup does initialize `standard` plugins if plugins.initialize is true', async () => {
config$.next({ plugins: { initialize: true } });
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
const { initialized } = await pluginsService.setup(setupDeps);
@ -1275,7 +1290,7 @@ describe('PluginsService', () => {
it('#setup does not initialize `standard` plugins if plugins.initialize is false', async () => {
config$.next({ plugins: { initialize: false } });
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
const { initialized } = await pluginsService.setup(setupDeps);
expect(standardMockPluginSystem.setupPlugins).not.toHaveBeenCalled();
@ -1312,7 +1327,7 @@ describe('PluginsService', () => {
it('does not try to stop `preboot` plugins and start `standard` ones if plugins.initialize is `false`', async () => {
config$.next({ plugins: { initialize: false } });
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
await pluginsService.setup(setupDeps);
@ -1325,7 +1340,7 @@ describe('PluginsService', () => {
});
it('stops `preboot` plugins and starts `standard` ones', async () => {
await pluginsService.discover({ environment: environmentPreboot });
await pluginsService.discover({ environment: environmentPreboot, node: nodePreboot });
await pluginsService.preboot(prebootDeps);
await pluginsService.setup(setupDeps);

View file

@ -15,6 +15,7 @@ import { Logger } from '@kbn/logging';
import type { IConfigService } from '@kbn/config';
import type { CoreContext, CoreService } from '@kbn/core-base-server-internal';
import type { PluginName } from '@kbn/core-base-common';
import type { InternalNodeServicePreboot } from '@kbn/core-node-server-internal';
import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery';
import { PluginWrapper } from './plugin';
import {
@ -84,6 +85,7 @@ export type PluginsServiceStartDeps = InternalCoreStart;
/** @internal */
export interface PluginsServiceDiscoverDeps {
environment: InternalEnvironmentServicePreboot;
node: InternalNodeServicePreboot;
}
/** @internal */
@ -109,11 +111,21 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS
this.standardPluginsSystem = new PluginsSystem(this.coreContext, PluginType.standard);
}
public async discover({ environment }: PluginsServiceDiscoverDeps): Promise<DiscoveredPlugins> {
public async discover({
environment,
node,
}: PluginsServiceDiscoverDeps): Promise<DiscoveredPlugins> {
const config = await firstValueFrom(this.config$);
const { error$, plugin$ } = discover(config, this.coreContext, {
uuid: environment.instanceUuid,
const { error$, plugin$ } = discover({
config,
coreContext: this.coreContext,
instanceInfo: {
uuid: environment.instanceUuid,
},
nodeInfo: {
roles: node.roles,
},
});
await this.handleDiscoveryErrors(error$);

View file

@ -18,6 +18,7 @@ import type {
ConfigDeprecationProvider,
} from '@kbn/config';
import type { PluginName, PluginOpaqueId, PluginType } from '@kbn/core-base-common';
import type { NodeInfo } from '@kbn/core-node-server';
import { ElasticsearchConfigType } from '../elasticsearch/elasticsearch_config';
import { SavedObjectsConfigType } from '../saved_objects/saved_objects_config';
@ -354,6 +355,29 @@ export interface PluginInitializerContext<ConfigSchema = unknown> {
instanceUuid: string;
configs: readonly string[];
};
/**
* Access the configuration for this particular Kibana node.
* Can be used to determine which `roles` the current process was started with.
*
* @example
* ```typescript
* // plugins/my-plugin/server/plugin.ts
*
* export class MyPlugin implements Plugin {
* constructor(private readonly initContext: PluginInitializerContext) {
* this.initContext = initContext;
* }
* setup() {
* if (this.initContext.node.roles.backgroundTasks) {
* // run background tasks
* } else if (this.initContext.node.roles.ui) {
* // register http routes, etc
* }
* }
* }
* ```
*/
node: NodeInfo;
/**
* {@link LoggerFactory | logger factory} instance already bound to the plugin's logging context
*

View file

@ -75,6 +75,13 @@ jest.doMock('./environment/environment_service', () => ({
EnvironmentService: jest.fn(() => mockEnvironmentService),
}));
import { nodeServiceMock } from '@kbn/core-node-server-mocks';
export const mockNodeService = nodeServiceMock.create();
jest.doMock('@kbn/core-node-server-internal', () => ({
NodeService: jest.fn(() => mockNodeService),
}));
import { metricsServiceMock } from './metrics/metrics_service.mock';
export const mockMetricsService = metricsServiceMock.create();

View file

@ -21,6 +21,7 @@ import {
mockLoggingService,
mockI18nService,
mockEnvironmentService,
mockNodeService,
mockPrebootService,
mockDeprecationService,
mockDocLinksService,
@ -62,6 +63,7 @@ test('preboot services on "preboot"', async () => {
const server = new Server(rawConfigService, env, logger);
expect(mockEnvironmentService.preboot).not.toHaveBeenCalled();
expect(mockNodeService.preboot).not.toHaveBeenCalled();
expect(mockContextService.preboot).not.toHaveBeenCalled();
expect(mockHttpService.preboot).not.toHaveBeenCalled();
expect(mockI18nService.preboot).not.toHaveBeenCalled();
@ -75,6 +77,7 @@ test('preboot services on "preboot"', async () => {
await server.preboot();
expect(mockEnvironmentService.preboot).toHaveBeenCalledTimes(1);
expect(mockNodeService.preboot).toHaveBeenCalledTimes(1);
expect(mockContextService.preboot).toHaveBeenCalledTimes(1);
expect(mockHttpService.preboot).toHaveBeenCalledTimes(1);
expect(mockI18nService.preboot).toHaveBeenCalledTimes(1);
@ -201,6 +204,7 @@ test('stops services on "stop"', async () => {
expect(mockHttpService.stop).not.toHaveBeenCalled();
expect(mockElasticsearchService.stop).not.toHaveBeenCalled();
expect(mockPluginsService.stop).not.toHaveBeenCalled();
expect(mockNodeService.stop).not.toHaveBeenCalled();
expect(mockSavedObjectsService.stop).not.toHaveBeenCalled();
expect(mockUiSettingsService.stop).not.toHaveBeenCalled();
expect(mockMetricsService.stop).not.toHaveBeenCalled();
@ -212,6 +216,7 @@ test('stops services on "stop"', async () => {
expect(mockHttpService.stop).toHaveBeenCalledTimes(1);
expect(mockElasticsearchService.stop).toHaveBeenCalledTimes(1);
expect(mockPluginsService.stop).toHaveBeenCalledTimes(1);
expect(mockNodeService.stop).toHaveBeenCalledTimes(1);
expect(mockSavedObjectsService.stop).toHaveBeenCalledTimes(1);
expect(mockUiSettingsService.stop).toHaveBeenCalledTimes(1);
expect(mockMetricsService.stop).toHaveBeenCalledTimes(1);

View file

@ -21,6 +21,7 @@ import {
coreDeprecationProvider,
ensureValidConfiguration,
} from '@kbn/core-config-server-internal';
import { NodeService, nodeConfig } from '@kbn/core-node-server-internal';
import { AnalyticsService } from '@kbn/core-analytics-server-internal';
import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server';
import { CoreApp } from './core_app';
@ -88,6 +89,7 @@ export class Server {
private readonly savedObjects: SavedObjectsService;
private readonly uiSettings: UiSettingsService;
private readonly environment: EnvironmentService;
private readonly node: NodeService;
private readonly metrics: MetricsService;
private readonly httpResources: HttpResourcesService;
private readonly status: StatusService;
@ -132,6 +134,7 @@ export class Server {
this.uiSettings = new UiSettingsService(core);
this.capabilities = new CapabilitiesService(core);
this.environment = new EnvironmentService(core);
this.node = new NodeService(core);
this.metrics = new MetricsService(core);
this.status = new StatusService(core);
this.coreApp = new CoreApp(core);
@ -159,9 +162,13 @@ export class Server {
const analyticsPreboot = this.analytics.preboot();
const environmentPreboot = await this.environment.preboot({ analytics: analyticsPreboot });
const nodePreboot = await this.node.preboot();
// Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph.
this.discoveredPlugins = await this.plugins.discover({ environment: environmentPreboot });
this.discoveredPlugins = await this.plugins.discover({
environment: environmentPreboot,
node: nodePreboot,
});
// Immediately terminate in case of invalid configuration. This needs to be done after plugin discovery. We also
// silent deprecation warnings until `setup` stage where we'll validate config once again.
@ -401,6 +408,7 @@ export class Server {
await this.metrics.stop();
await this.status.stop();
await this.logging.stop();
this.node.stop();
this.deprecations.stop();
}
@ -412,22 +420,23 @@ export class Server {
public setupCoreConfig() {
const configDescriptors: Array<ServiceConfigDescriptor<unknown>> = [
executionContextConfig,
pathConfig,
cspConfig,
deprecationConfig,
elasticsearchConfig,
executionContextConfig,
externalUrlConfig,
loggingConfig,
httpConfig,
i18nConfig,
loggingConfig,
nodeConfig,
opsConfig,
pathConfig,
pidConfig,
pluginsConfig,
savedObjectsConfig,
savedObjectsMigrationConfig,
uiSettingsConfig,
opsConfig,
statusConfig,
pidConfig,
i18nConfig,
deprecationConfig,
uiSettingsConfig,
];
this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider);

View file

@ -126,6 +126,7 @@ kibana_vars=(
monitoring.ui.max_bucket_size
monitoring.ui.min_interval_seconds
newsfeed.enabled
node.roles
ops.cGroupOverrides.cpuAcctPath
ops.cGroupOverrides.cpuPath
ops.interval

View 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 { FtrConfigProviderContext } from '@kbn/test';
import path from 'path';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js'));
return {
rootTags: ['runOutsideOfCiGroups'],
testFiles: [require.resolve('./test_suites/all')],
services: {
...functionalConfig.get('services'),
},
pageObjects: functionalConfig.get('pageObjects'),
servers: functionalConfig.get('servers'),
esTestCluster: {
...functionalConfig.get('esTestCluster'),
serverArgs: ['xpack.security.enabled=false'],
},
apps: functionalConfig.get('apps'),
screenshots: functionalConfig.get('screenshots'),
junit: {
reportName: 'Plugin Functional Tests - node roles - all',
},
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Required to load new platform plugins via `--plugin-path` flag.
'--env.name=development',
// for testing set buffer duration to 0 to immediately flush counters into saved objects.
'--usageCollection.usageCounters.bufferDuration=0',
`--plugin-path=${path.resolve(__dirname, 'plugins', 'core_plugin_initializer_context')}`,
'--node.roles=["*"]',
],
},
};
}

View 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 { FtrConfigProviderContext } from '@kbn/test';
import path from 'path';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js'));
return {
rootTags: ['runOutsideOfCiGroups'],
testFiles: [require.resolve('./test_suites/background_tasks')],
services: {
...functionalConfig.get('services'),
},
pageObjects: functionalConfig.get('pageObjects'),
servers: functionalConfig.get('servers'),
esTestCluster: {
...functionalConfig.get('esTestCluster'),
serverArgs: ['xpack.security.enabled=false'],
},
apps: functionalConfig.get('apps'),
screenshots: functionalConfig.get('screenshots'),
junit: {
reportName: 'Plugin Functional Tests - node roles - background tasks',
},
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Required to load new platform plugins via `--plugin-path` flag.
'--env.name=development',
// for testing set buffer duration to 0 to immediately flush counters into saved objects.
'--usageCollection.usageCounters.bufferDuration=0',
`--plugin-path=${path.resolve(__dirname, 'plugins', 'core_plugin_initializer_context')}`,
'--node.roles=["background_tasks"]',
],
},
};
}

View file

@ -0,0 +1,12 @@
{
"id": "corePluginInitializerContext",
"version": "0.0.1",
"kibanaVersion": "kibana",
"owner": {
"name": "Core",
"githubTeam": "kibana-core"
},
"configPath": ["core_plugin_initializer_context"],
"server": true,
"ui": false
}

View file

@ -0,0 +1,14 @@
{
"name": "core_plugin_initializer_context",
"version": "1.0.0",
"main": "target/test/plugin_functional/plugins/core_plugin_initializer_context",
"kibana": {
"version": "kibana",
"templateVersion": "1.0.0"
},
"license": "SSPL-1.0 OR Elastic License 2.0",
"scripts": {
"kbn": "node ../../../../scripts/kbn.js",
"build": "rm -rf './target' && ../../../../node_modules/.bin/tsc"
}
}

View file

@ -0,0 +1,13 @@
/*
* 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 type { PluginInitializerContext } from '@kbn/core/server';
import { CorePluginInitializerContextPlugin } from './plugin';
export const plugin = (initializerContext: PluginInitializerContext) =>
new CorePluginInitializerContextPlugin(initializerContext);

View file

@ -0,0 +1,36 @@
/*
* 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 type { Plugin, CoreSetup, PluginInitializerContext } from '@kbn/core/server';
export class CorePluginInitializerContextPlugin implements Plugin {
readonly initializerContext: PluginInitializerContext;
constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
}
public setup(core: CoreSetup, deps: {}) {
const router = core.http.createRouter();
router.get(
{
path: '/core_plugin_initializer_context/node/roles',
validate: false,
options: {
authRequired: false,
},
},
async (context, req, res) => {
return res.ok({ body: this.initializerContext.node.roles });
}
);
}
public start() {}
public stop() {}
}

View file

@ -0,0 +1,15 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target/types"
},
"include": [
"index.ts",
"server/**/*.ts",
"../../../../typings/**/*",
],
"exclude": [],
"references": [
{ "path": "../../../../src/core/tsconfig.json" }
]
}

View file

@ -0,0 +1,11 @@
/*
* 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 { FtrProviderContext } from '../../functional/ftr_provider_context';
export type PluginFunctionalProviderContext = FtrProviderContext;

View file

@ -0,0 +1,15 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
describe('core plugins - initializer context - node roles - all', () => {
loadTestFile(require.resolve('./initializer_context'));
});
}

View file

@ -0,0 +1,23 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
import '@kbn/core-provider-plugin/types';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
describe('initializer context', () => {
it('passes node roles to server PluginInitializerContext', async () => {
await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, {
backgroundTasks: true,
ui: true,
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
describe('core plugins - initializer context - node roles - backgroundTasks', () => {
loadTestFile(require.resolve('./initializer_context'));
});
}

View file

@ -0,0 +1,23 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
import '@kbn/core-provider-plugin/types';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
describe('initializer context', () => {
it('passes node roles to server PluginInitializerContext', async () => {
await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, {
backgroundTasks: true,
ui: false,
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
export default function ({ loadTestFile }: PluginFunctionalProviderContext) {
describe('core plugins - initializer context - node roles - ui', () => {
loadTestFile(require.resolve('./initializer_context'));
});
}

View file

@ -0,0 +1,23 @@
/*
* 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 { PluginFunctionalProviderContext } from '../../services';
import '@kbn/core-provider-plugin/types';
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const supertest = getService('supertest');
describe('initializer context', () => {
it('passes node roles to server PluginInitializerContext', async () => {
await supertest.get('/core_plugin_initializer_context/node/roles').expect(200, {
backgroundTasks: false,
ui: true,
});
});
});
}

View 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 { FtrConfigProviderContext } from '@kbn/test';
import path from 'path';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../functional/config.base.js'));
return {
rootTags: ['runOutsideOfCiGroups'],
testFiles: [require.resolve('./test_suites/ui')],
services: {
...functionalConfig.get('services'),
},
pageObjects: functionalConfig.get('pageObjects'),
servers: functionalConfig.get('servers'),
esTestCluster: {
...functionalConfig.get('esTestCluster'),
serverArgs: ['xpack.security.enabled=false'],
},
apps: functionalConfig.get('apps'),
screenshots: functionalConfig.get('screenshots'),
junit: {
reportName: 'Plugin Functional Tests - node roles - ui',
},
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Required to load new platform plugins via `--plugin-path` flag.
'--env.name=development',
// for testing set buffer duration to 0 to immediately flush counters into saved objects.
'--usageCollection.usageCounters.bufferDuration=0',
`--plugin-path=${path.resolve(__dirname, 'plugins', 'core_plugin_initializer_context')}`,
'--node.roles=["ui"]',
],
},
};
}

View file

@ -3167,6 +3167,18 @@
version "0.0.0"
uid ""
"@kbn/core-node-server-internal@link:bazel-bin/packages/core/node/core-node-server-internal":
version "0.0.0"
uid ""
"@kbn/core-node-server-mocks@link:bazel-bin/packages/core/node/core-node-server-mocks":
version "0.0.0"
uid ""
"@kbn/core-node-server@link:bazel-bin/packages/core/node/core-node-server":
version "0.0.0"
uid ""
"@kbn/core-theme-browser-internal@link:bazel-bin/packages/core/theme/core-theme-browser-internal":
version "0.0.0"
uid ""
@ -6634,6 +6646,18 @@
version "0.0.0"
uid ""
"@types/kbn__core-node-server-internal@link:bazel-bin/packages/core/node/core-node-server-internal/npm_module_types":
version "0.0.0"
uid ""
"@types/kbn__core-node-server-mocks@link:bazel-bin/packages/core/node/core-node-server-mocks/npm_module_types":
version "0.0.0"
uid ""
"@types/kbn__core-node-server@link:bazel-bin/packages/core/node/core-node-server/npm_module_types":
version "0.0.0"
uid ""
"@types/kbn__core-public-internal-base@link:bazel-bin/packages/core/public/internal-base/npm_module_types":
version "0.0.0"
uid ""