[Code] migrate Code config/logging/feature service to new platform (#46664) (#47253)

* [Code] code backend NP migration

* Move code plugin config to NP config service

* Move code plugin logger to NP logger service

* minor type error

* remove joi config

* addressing comments

* fix unit tests

* Migrate to xpack feature service for NP

* minor comments

* fix code mocha test scripts

* fix type

* fix i18nrc.json for code
This commit is contained in:
Mengwei Ding 2019-10-03 12:35:58 -07:00 committed by GitHub
parent c3edee5415
commit 9ec4e89828
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 354 additions and 204 deletions

View file

@ -7,7 +7,7 @@
"xpack.apm": "legacy/plugins/apm",
"xpack.beatsManagement": "legacy/plugins/beats_management",
"xpack.canvas": "legacy/plugins/canvas",
"xpack.code": "legacy/plugins/code",
"xpack.code": ["legacy/plugins/code", "plugins/code"],
"xpack.crossClusterReplication": "legacy/plugins/cross_cluster_replication",
"xpack.dashboardMode": "legacy/plugins/dashboard_mode",
"xpack.features": "plugins/features",

View file

@ -7,14 +7,11 @@
import { RequestQuery, ResponseToolkit, RouteOptions, ServerRoute } from 'hapi';
import JoiNamespace from 'joi';
import { Legacy } from 'kibana';
import moment from 'moment';
import { resolve } from 'path';
import { CoreSetup } from 'src/core/server';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { APP_TITLE } from './common/constants';
import { codePlugin } from './server';
import { DEFAULT_WATERMARK_LOW_PERCENTAGE } from './server/disk_watermark';
import { LanguageServers, LanguageServersDeveloping } from './server/lsp/language_servers';
export type RequestFacade = Legacy.Request;
export type RequestQueryFacade = RequestQuery;
@ -25,7 +22,7 @@ export type ServerRouteFacade = ServerRoute;
export const code = (kibana: any) =>
new kibana.Plugin({
require: ['kibana', 'elasticsearch', 'xpack_main'],
require: ['kibana', 'elasticsearch'],
id: 'code',
configPrefix: 'xpack.code',
publicDir: resolve(__dirname, 'public'),
@ -46,98 +43,29 @@ export const code = (kibana: any) =>
hacks: ['plugins/code/hacks/toggle_app_link_in_nav'],
},
config(Joi: typeof JoiNamespace) {
const langSwitches: any = {};
LanguageServers.forEach(lang => {
langSwitches[lang.name] = Joi.object({
enabled: Joi.boolean().default(true),
});
});
LanguageServersDeveloping.forEach(lang => {
langSwitches[lang.name] = Joi.object({
enabled: Joi.boolean().default(false),
});
});
return Joi.object({
// Still keep this config item here for the injectDefaultVars
// in line 40 here.
ui: Joi.object({
enabled: Joi.boolean().default(true),
}).default(),
enabled: Joi.boolean().default(true),
queueIndex: Joi.string().default('.code_internal-worker-queue'),
// 1 hour by default.
queueTimeoutMs: Joi.number().default(moment.duration(1, 'hour').asMilliseconds()),
// The frequency which update scheduler executes. 1 minute by default.
updateFrequencyMs: Joi.number().default(moment.duration(1, 'minute').asMilliseconds()),
// The frequency which index scheduler executes. 1 day by default.
indexFrequencyMs: Joi.number().default(moment.duration(1, 'day').asMilliseconds()),
// The frequency which each repo tries to update. 5 minutes by default.
updateRepoFrequencyMs: Joi.number().default(moment.duration(5, 'minute').asMilliseconds()),
// The frequency which each repo tries to index. 1 day by default.
indexRepoFrequencyMs: Joi.number().default(moment.duration(1, 'day').asMilliseconds()),
// whether we want to show more logs
verbose: Joi.boolean().default(false),
lsp: Joi.object({
...langSwitches,
// timeout of a request
requestTimeoutMs: Joi.number().default(moment.duration(10, 'second').asMilliseconds()),
// if we want the language server run in seperately
detach: Joi.boolean().default(false),
// enable oom_score_adj on linux
oomScoreAdj: Joi.boolean().default(true),
}).default(),
repos: Joi.array().default([]),
security: Joi.object({
enableMavenImport: Joi.boolean().default(true),
enableGradleImport: Joi.boolean().default(false),
installGoDependency: Joi.boolean().default(false),
installNodeDependency: Joi.boolean().default(true),
gitHostWhitelist: Joi.array()
.items(Joi.string())
.default([
'github.com',
'gitlab.com',
'bitbucket.org',
'gitbox.apache.org',
'eclipse.org',
]),
gitProtocolWhitelist: Joi.array()
.items(Joi.string())
.default(['https', 'git', 'ssh']),
enableGitCertCheck: Joi.boolean().default(true),
}).default(),
disk: Joi.object({
thresholdEnabled: Joi.bool().default(true),
watermarkLow: Joi.string().default(`${DEFAULT_WATERMARK_LOW_PERCENTAGE}%`),
}).default(),
maxWorkspace: Joi.number().default(5), // max workspace folder for each language server
enableGlobalReference: Joi.boolean().default(false), // Global reference as optional feature for now
enableCommitIndexing: Joi.boolean().default(false),
codeNodeUrl: Joi.string(),
clustering: Joi.object({
enabled: Joi.bool().default(false),
codeNodes: Joi.array()
.items(
Joi.object({
id: Joi.string(),
address: Joi.string(),
})
)
.default([]),
}).default(),
}).default();
},
init(server: ServerFacade, options: any) {
if (!options.ui.enabled) {
async init(server: ServerFacade) {
// @ts-ignore
const initializerContext = server.newPlatform.setup.plugins.code;
if (!initializerContext.legacy.config.ui.enabled) {
return;
}
const initializerContext = {} as PluginInitializerContext;
const coreSetup = ({
http: { server },
} as any) as CoreSetup;
// Set up with the new platform plugin lifecycle API.
const plugin = codePlugin(initializerContext);
plugin.setup(coreSetup, options);
plugin.setup(coreSetup);
// @ts-ignore
const kbnServer = this.kbnServer;

View file

@ -3,8 +3,5 @@
"version": "kibana",
"server": true,
"ui": true,
"requiredPlugins": [
"elasticsearch",
"xpack_main"
]
"requiredPlugins": ["elasticsearch"]
}

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
require('./_helpers').runXPackScript('mocha', ['legacy/plugins/code/server/__tests__/*.{ts,tsx}']);
require('./_helpers').runKibanaScript('mocha', ['x-pack/legacy/plugins/code/server/__tests__/*.{ts,tsx}']);

View file

@ -4,12 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Server } from 'hapi';
import { ServiceHandlerFor } from '../service_definition';
import { WorkspaceHandler } from '../../lsp/workspace_handler';
import { RepoConfig } from '../../../model';
import { WorkspaceCommand } from '../../lsp/workspace_command';
import { Logger } from '../../log';
import { LoggerFactory as CodeLoggerFactory } from '../../utils/log_factory';
export const WorkspaceDefinition = {
initCmd: {
@ -19,7 +18,7 @@ export const WorkspaceDefinition = {
};
export const getWorkspaceHandler = (
server: Server,
loggerFactory: CodeLoggerFactory,
workspaceHandler: WorkspaceHandler
): ServiceHandlerFor<typeof WorkspaceDefinition> => ({
async initCmd({ repoUri, revision, repoConfig, force }) {
@ -28,9 +27,13 @@ export const getWorkspaceHandler = (
repoUri,
revision
);
const log = new Logger(server, ['workspace', repoUri]);
const workspaceCmd = new WorkspaceCommand(repoConfig, workspaceDir, workspaceRevision, log);
const workspaceCmd = new WorkspaceCommand(
repoConfig,
workspaceDir,
workspaceRevision,
loggerFactory.getLogger(['workspace', repoUri])
);
await workspaceCmd.runInit(force);
return {};
} catch (e) {

View file

@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { Request, Server } from 'hapi';
import { createTestHapiServer } from '../test_utils';
import { LocalHandlerAdapter } from './local_handler_adapter';
@ -13,9 +14,10 @@ import { DEFAULT_SERVICE_OPTION } from './service_handler_adapter';
import { NonCodeNodeAdapter } from './multinode/non_code_node_adapter';
import { CodeServices } from './code_services';
import { Logger } from '../log';
import { ConsoleLoggerFactory } from '../utils/console_logger_factory';
const log: Logger = new ConsoleLoggerFactory().getLogger(['test']);
let hapiServer: Server = createTestHapiServer();
const log = new Logger(hapiServer);
let server: CodeServerRouter = new CodeServerRouter(hapiServer);
beforeEach(async () => {

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PluginInitializerContext } from 'src/core/server';
import * as constants from '../common/constants';
import { CodePlugin } from './plugin';
import { PluginSetupContract } from '../../../../plugins/code/server/index';
export const codePlugin = (initializerContext: PluginInitializerContext) =>
export const codePlugin = (initializerContext: PluginSetupContract) =>
new CodePlugin(initializerContext);
export { constants };

View file

@ -5,12 +5,13 @@
*/
import { Server } from 'hapi';
import { LoggerFactory } from 'src/core/server';
import { ServerOptions } from './server_options';
import { CodeServices } from './distributed/code_services';
import { EsClient } from './lib/esqueue';
import { RepositoryConfigController } from './repository_config_controller';
import { GitOperations } from './git_operations';
import { Logger } from './log';
import {
getGitServiceHandler,
getLspServiceHandler,
@ -29,7 +30,7 @@ import { ServerLoggerFactory } from './utils/server_logger_factory';
export function initLocalService(
server: Server,
log: Logger,
loggerFactory: LoggerFactory,
serverOptions: ServerOptions,
codeServices: CodeServices,
esClient: EsClient,
@ -43,6 +44,7 @@ export function initLocalService(
GitServiceDefinitionOption
);
const serverLoggerFactory = new ServerLoggerFactory(loggerFactory, serverOptions.verbose);
const installManager = new InstallManager(server, serverOptions);
const lspService = new LspService(
'127.0.0.1',
@ -50,11 +52,11 @@ export function initLocalService(
gitOps,
esClient,
installManager,
new ServerLoggerFactory(server),
serverLoggerFactory,
repoConfigController
);
server.events.on('stop', async () => {
log.debug('shutdown lsp process');
loggerFactory.get().debug('shutdown lsp process');
await lspService.shutdown();
await gitOps.cleanAllRepo();
});
@ -65,7 +67,7 @@ export function initLocalService(
);
codeServices.registerHandler(
WorkspaceDefinition,
getWorkspaceHandler(server, lspService.workspaceHandler)
getWorkspaceHandler(serverLoggerFactory, lspService.workspaceHandler)
);
codeServices.registerHandler(SetupDefinition, setupServiceHandler);

View file

@ -4,13 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Server } from 'hapi';
import { EsClient, Esqueue } from './lib/esqueue';
import { Logger } from './log';
import { ServerOptions } from './server_options';
export function initQueue(server: Server, log: Logger, esClient: EsClient) {
const queueIndex: string = server.config().get('xpack.code.queueIndex');
const queueTimeoutMs: number = server.config().get('xpack.code.queueTimeoutMs');
export function initQueue(serverOptions: ServerOptions, log: Logger, esClient: EsClient) {
const queueIndex: string = serverOptions.queueIndex;
const queueTimeoutMs: number = serverOptions.queueTimeoutMs;
const queue = new Esqueue(queueIndex, {
client: esClient,
timeout: queueTimeoutMs,

View file

@ -7,19 +7,22 @@
import { inspect } from 'util';
import { Logger as VsLogger } from 'vscode-jsonrpc';
import { ServerFacade } from '..';
import { Logger as KibanaLogger, LoggerFactory } from 'src/core/server';
export class Logger implements VsLogger {
private readonly verbose: boolean = false;
constructor(private server: ServerFacade, private baseTags: string[] = ['code']) {
if (server) {
this.verbose = this.server.config().get('xpack.code.verbose');
}
private logger: KibanaLogger;
constructor(
private readonly loggerFactory: LoggerFactory,
private readonly verbose: boolean = false,
private readonly baseTags: string[] = []
) {
this.logger = this.loggerFactory.get(...this.baseTags);
}
// Return a new logger with new tags
public addTags(tags: string[]): Logger {
return new Logger(this.server, this.baseTags.concat(tags));
return new Logger(this.loggerFactory, this.verbose, this.baseTags.concat(tags));
}
public info(msg: string | any) {
@ -28,7 +31,7 @@ export class Logger implements VsLogger {
colors: process.stdout.isTTY,
});
}
this.server.log([...this.baseTags, 'info'], msg);
this.logger.info(msg);
}
public error(msg: string | any) {
@ -42,11 +45,11 @@ export class Logger implements VsLogger {
});
}
this.server.log([...this.baseTags, 'error'], msg);
this.logger.error(msg);
}
public log(message: string): void {
this.info(message);
public log(msg: string): void {
this.logger.info(msg);
}
public debug(msg: string | any) {
@ -56,9 +59,9 @@ export class Logger implements VsLogger {
});
}
if (this.verbose) {
this.server.log([...this.baseTags, 'info'], msg);
this.logger.info(msg);
} else {
this.server.log([...this.baseTags, 'debug'], msg);
this.logger.debug(msg);
}
}
@ -73,7 +76,7 @@ export class Logger implements VsLogger {
});
}
this.server.log([...this.baseTags, 'warning'], msg);
this.logger.warn(msg);
}
// Log subprocess stdout
@ -84,9 +87,9 @@ export class Logger implements VsLogger {
});
}
if (this.verbose) {
this.server.log([...this.baseTags, 'info', 'stdout'], msg);
this.logger.info(msg);
} else {
this.server.log([...this.baseTags, 'debug', 'stdout'], msg);
this.logger.debug(msg);
}
}
@ -98,9 +101,9 @@ export class Logger implements VsLogger {
});
}
if (this.verbose) {
this.server.log([...this.baseTags, 'error', 'stderr'], msg);
this.logger.error(msg);
} else {
this.server.log([...this.baseTags, 'debug', 'stderr'], msg);
this.logger.debug(msg);
}
}
}

View file

@ -29,7 +29,21 @@ const workspaceDir = path.join(baseDir, 'workspace');
// @ts-ignore
const options: ServerOptions = sinon.createStubInstance(ServerOptions);
// @ts-ignore
options.lsp = { detach: false };
options.lsp = {
TypeScript: {
enabled: true,
},
Java: {
enabled: true,
},
Go: {
enabled: true,
},
Ctags: {
enabled: true,
},
detach: false,
};
// @ts-ignore
options.maxWorkspace = 2;
const server = createTestHapiServer();

View file

@ -57,7 +57,7 @@ export class LanguageServerController implements ILanguageServerHandler {
readonly repoConfigController: RepositoryConfigController
) {
this.log = loggerFactory.getLogger([]);
this.languageServers = enabledLanguageServers(installManager.server).map(def => ({
this.languageServers = enabledLanguageServers(options).map(def => ({
definition: def,
builtinWorkspaceFolders: def.builtinWorkspaceFolders,
languages: def.languages,

View file

@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ServerFacade } from '../..';
import { InstallationType } from '../../common/installation';
import { CTAGS_SUPPORT_LANGS, LanguageServer } from '../../common/language_server';
import { ServerOptions } from '../server_options';
import { CtagsLauncher } from './ctags_launcher';
import { GoServerLauncher } from './go_launcher';
import { JavaLauncher } from './java_launcher';
@ -72,12 +72,13 @@ export const CTAGS: LanguageServerDefinition = {
export const LanguageServers: LanguageServerDefinition[] = [TYPESCRIPT, JAVA, GO, CTAGS];
export const LanguageServersDeveloping: LanguageServerDefinition[] = [];
export function enabledLanguageServers(server: ServerFacade) {
const devMode: boolean = server.config().get('env.dev');
export function enabledLanguageServers(serverOptions: ServerOptions) {
const devMode: boolean = serverOptions.devMode;
function isEnabled(lang: LanguageServerDefinition, defaultEnabled: boolean) {
const name = lang.name;
const enabled = server.config().get(`xpack.code.lsp.${name}.enabled`);
// @ts-ignore
const enabled = serverOptions.lsp[name] && serverOptions.lsp[name].enabled;
return enabled === undefined ? defaultEnabled : enabled;
}
const results = LanguageServers.filter(lang => isEnabled(lang, true));

View file

@ -4,12 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import crypto from 'crypto';
import * as _ from 'lodash';
import { CoreSetup, PluginInitializerContext } from 'src/core/server';
import { CoreSetup } from 'src/core/server';
import { XPackMainPlugin } from '../../xpack_main/xpack_main';
import { GitOperations } from './git_operations';
import { RepositoryIndexInitializerFactory, tryMigrateIndices } from './indexer';
import { Esqueue } from './lib/esqueue';
@ -56,6 +54,7 @@ import { initWorkers } from './init_workers';
import { ClusterNodeAdapter } from './distributed/cluster/cluster_node_adapter';
import { NodeRepositoriesService } from './distributed/cluster/node_repositories_service';
import { initCodeUsageCollector } from './usage_collector';
import { PluginSetupContract } from '../../../../plugins/code/server/index';
export class CodePlugin {
private isCodeNode = false;
@ -70,50 +69,15 @@ export class CodePlugin {
private codeServices: CodeServices | null = null;
private nodeService: NodeRepositoriesService | null = null;
constructor(initializerContext: PluginInitializerContext) {
constructor(private readonly initContext: PluginSetupContract) {
this.log = {} as Logger;
this.serverOptions = {} as ServerOptions;
}
// TODO: options is not a valid param for the setup() api
// of the new platform. Will need to pass through the configs
// correctly in the new platform.
public setup(core: CoreSetup, options: any) {
public setup(core: CoreSetup) {
const { server } = core.http as any;
this.log = new Logger(server);
this.serverOptions = new ServerOptions(options, server.config());
const xpackMainPlugin: XPackMainPlugin = server.plugins.xpack_main;
xpackMainPlugin.registerFeature({
id: 'code',
name: i18n.translate('xpack.code.featureRegistry.codeFeatureName', {
defaultMessage: 'Code',
}),
icon: 'codeApp',
navLinkId: 'code',
app: ['code', 'kibana'],
catalogue: [], // TODO add catalogue here
privileges: {
all: {
excludeFromBasePrivileges: true,
api: ['code_user', 'code_admin'],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show', 'user', 'admin'],
},
read: {
api: ['code_user'],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show', 'user'],
},
},
});
this.serverOptions = new ServerOptions(this.initContext.legacy.config, server.config());
this.log = new Logger(this.initContext.legacy.logger, this.serverOptions.verbose);
}
// TODO: CodeStart will not have the register route api.
@ -165,11 +129,11 @@ export class CodePlugin {
const codeServices = new CodeServices(clusterNodeAdapter);
this.queue = initQueue(server, this.log, esClient);
this.queue = initQueue(this.serverOptions, this.log, esClient);
const { gitOps, lspService } = initLocalService(
server,
this.log,
this.initContext.legacy.logger,
this.serverOptions,
codeServices,
esClient,
@ -213,11 +177,11 @@ export class CodePlugin {
this.log
);
this.queue = initQueue(server, this.log, esClient);
this.queue = initQueue(this.serverOptions, this.log, esClient);
const { gitOps, lspService } = initLocalService(
server,
this.log,
this.initContext.legacy.logger,
this.serverOptions,
codeServices,
esClient,
@ -293,7 +257,8 @@ export class CodePlugin {
codeServices,
repoIndexInitializerFactory,
repoConfigController,
this.serverOptions
this.serverOptions,
this.log
);
repositorySearchRoute(codeServerRouter, this.log);
if (this.serverOptions.enableCommitIndexing) {
@ -304,8 +269,8 @@ export class CodePlugin {
fileRoute(codeServerRouter, codeServices);
workspaceRoute(codeServerRouter, this.serverOptions, codeServices);
symbolByQnameRoute(codeServerRouter, this.log);
installRoute(codeServerRouter, codeServices);
lspRoute(codeServerRouter, codeServices, this.serverOptions);
installRoute(codeServerRouter, codeServices, this.serverOptions);
lspRoute(codeServerRouter, codeServices, this.serverOptions, this.log);
setupRoute(codeServerRouter, codeServices);
statusRoute(codeServerRouter, codeServices);
}
@ -342,7 +307,7 @@ export class CodePlugin {
private initDevMode(server: any) {
// @ts-ignore
const devMode: boolean = server.config().get('env.dev');
const devMode: boolean = this.serverOptions.devMode;
server.injectUiAppVars('code', () => ({
enableLangserversDeveloping: devMode,
}));

View file

@ -12,8 +12,13 @@ import { CodeServerRouter } from '../security';
import { CodeServices } from '../distributed/code_services';
import { LspServiceDefinition } from '../distributed/apis';
import { Endpoint } from '../distributed/resource_locator';
import { ServerOptions } from '../server_options';
export function installRoute(router: CodeServerRouter, codeServices: CodeServices) {
export function installRoute(
router: CodeServerRouter,
codeServices: CodeServices,
options: ServerOptions
) {
const lspService = codeServices.serviceFor(LspServiceDefinition);
const kibanaVersion = router.server.config().get('pkg.version') as string;
const status = async (endpoint: Endpoint, def: LanguageServerDefinition) => ({
@ -32,9 +37,7 @@ export function installRoute(router: CodeServerRouter, codeServices: CodeService
path: '/api/code/install',
async handler(req: RequestFacade) {
const endpoint = await codeServices.locate(req, '');
return await Promise.all(
enabledLanguageServers(router.server).map(def => status(endpoint, def))
);
return await Promise.all(enabledLanguageServers(options).map(def => status(endpoint, def)));
},
method: 'GET',
});
@ -43,7 +46,7 @@ export function installRoute(router: CodeServerRouter, codeServices: CodeService
path: '/api/code/install/{name}',
async handler(req: RequestFacade) {
const name = req.params.name;
const def = enabledLanguageServers(router.server).find(d => d.name === name);
const def = enabledLanguageServers(options).find(d => d.name === name);
const endpoint = await codeServices.locate(req, '');
if (def) {
return await status(endpoint, def);

View file

@ -7,8 +7,8 @@
import Boom from 'boom';
import { ResponseError } from 'vscode-jsonrpc';
import { ResponseMessage } from 'vscode-jsonrpc/lib/messages';
import { SymbolLocator } from '@elastic/lsp-extension';
import {
LanguageServerStartFailed,
ServerNotInitialized,
@ -32,9 +32,9 @@ const LANG_SERVER_ERROR = 'language server error';
export function lspRoute(
server: CodeServerRouter,
codeServices: CodeServices,
serverOptions: ServerOptions
serverOptions: ServerOptions,
log: Logger
) {
const log = new Logger(server.server);
const lspService = codeServices.serviceFor(LspServiceDefinition);
const gitService = codeServices.serviceFor(GitServiceDefinition);

View file

@ -25,7 +25,8 @@ export function repositoryRoute(
codeServices: CodeServices,
repoIndexInitializerFactory: RepositoryIndexInitializerFactory,
repoConfigController: RepositoryConfigController,
options: ServerOptions
options: ServerOptions,
log: Logger
) {
const repositoryService = codeServices.serviceFor(RepositoryServiceDefinition);
// Clone a git repository
@ -35,7 +36,6 @@ export function repositoryRoute(
method: 'POST',
async handler(req: RequestFacade, h: ResponseToolkitFacade) {
const repoUrl: string = (req.payload as any).url;
const log = new Logger(req.server);
// Reject the request if the url is an invalid git url.
try {
@ -104,7 +104,6 @@ export function repositoryRoute(
method: 'DELETE',
async handler(req: RequestFacade, h: ResponseToolkitFacade) {
const repoUri: string = req.params.uri as string;
const log = new Logger(req.server);
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
try {
// Check if the repository already exists. If not, an error will be thrown.
@ -146,7 +145,6 @@ export function repositoryRoute(
method: 'GET',
async handler(req: RequestFacade) {
const repoUri = req.params.uri as string;
const log = new Logger(req.server);
try {
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
return await repoObjectClient.getRepository(repoUri);
@ -164,7 +162,6 @@ export function repositoryRoute(
method: 'GET',
async handler(req: RequestFacade) {
const repoUri = req.params.uri as string;
const log = new Logger(req.server);
try {
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
let gitStatus = null;
@ -206,7 +203,6 @@ export function repositoryRoute(
path: '/api/code/repos',
method: 'GET',
async handler(req: RequestFacade) {
const log = new Logger(req.server);
try {
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
return await repoObjectClient.getAllRepositories();
@ -228,7 +224,6 @@ export function repositoryRoute(
requireAdmin: true,
async handler(req: RequestFacade) {
const repoUri = req.params.uri as string;
const log = new Logger(req.server);
const reindex: boolean = (req.payload as any).reindex;
try {
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
@ -259,7 +254,6 @@ export function repositoryRoute(
async handler(req: RequestFacade) {
const config: RepositoryConfig = req.payload as RepositoryConfig;
const repoUri: RepositoryUri = config.uri;
const log = new Logger(req.server);
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
try {

View file

@ -30,6 +30,8 @@ export interface DiskOptions {
}
export class ServerOptions {
public readonly devMode: boolean = this.config.get('env.dev');
public readonly workspacePath = resolve(this.config.get('path.data'), 'code/workspace');
public readonly repoPath = resolve(this.config.get('path.data'), 'code/repos');
@ -76,8 +78,13 @@ export class ServerOptions {
public readonly clusterEnabled: boolean = this.options.clustering.enabled;
public readonly verbose: boolean = this.options.verbose;
public readonly codeNodes: CodeNode[] = this.options.clustering.codeNodes;
public readonly queueIndex: string = this.options.queueIndex;
public readonly queueTimeoutMs: number = this.options.queueTimeoutMs;
constructor(private options: any, private config: any) {}
/**

View file

@ -9,8 +9,11 @@ import { Logger } from '../log';
export class ConsoleLogger extends Logger {
constructor() {
// @ts-ignore
super(undefined);
super({
get: (...contextParts: string[]) => {
return console as any;
},
});
}
public info(msg: string | any) {

View file

@ -9,7 +9,7 @@ import { ConsoleLogger } from './console_logger';
import { LoggerFactory } from './log_factory';
export class ConsoleLoggerFactory implements LoggerFactory {
public getLogger(tags: string[]): Logger {
public getLogger(tags: string[] = []): Logger {
return new ConsoleLogger();
}
}

View file

@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { LoggerFactory } from 'src/core/server';
import { Logger } from '../log';
import { LoggerFactory } from './log_factory';
import { ServerFacade } from '../..';
import { LoggerFactory as CodeLoggerFactory } from './log_factory';
export class ServerLoggerFactory implements LoggerFactory {
constructor(private readonly server: ServerFacade) {}
export class ServerLoggerFactory implements CodeLoggerFactory {
constructor(private readonly loggerFactory: LoggerFactory, private readonly verbose: boolean) {}
public getLogger(tags: string[]): Logger {
return new Logger(this.server, tags);
public getLogger(tags: string[] = []): Logger {
return new Logger(this.loggerFactory, this.verbose, tags);
}
}

View file

@ -0,0 +1,9 @@
{
"id": "code",
"version": "8.0.0",
"kibanaVersion": "kibana",
"configPath": ["x-pack", "code"],
"server": true,
"ui": false,
"requiredPlugins": ["features"]
}

View file

@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { schema } from '@kbn/config-schema';
import moment from 'moment';
// TODO: update these legacy imports to new ones.
import {
LanguageServers,
LanguageServersDeveloping,
} from '../../../legacy/plugins/code/server/lsp/language_servers';
import { DEFAULT_WATERMARK_LOW_PERCENTAGE } from '../../../legacy/plugins/code/server/disk_watermark';
const createCodeConfigSchema = () => {
const langSwitches: any = {};
LanguageServers.forEach(lang => {
langSwitches[lang.name] = schema.object({
enabled: schema.boolean({ defaultValue: true }),
});
});
LanguageServersDeveloping.forEach(lang => {
langSwitches[lang.name] = schema.object({
enabled: schema.boolean({ defaultValue: false }),
});
});
return schema.object({
ui: schema.object({
enabled: schema.boolean({ defaultValue: true }),
}),
enabled: schema.boolean({ defaultValue: true }),
queueIndex: schema.string({ defaultValue: '.code_internal-worker-queue' }),
// 1 hour by default.
queueTimeoutMs: schema.number({
defaultValue: moment.duration(1, 'hour').asMilliseconds(),
}),
// The frequency which update scheduler executes. 1 minute by default.
updateFrequencyMs: schema.number({
defaultValue: moment.duration(1, 'minute').asMilliseconds(),
}),
// The frequency which index scheduler executes. 1 day by default.
indexFrequencyMs: schema.number({
defaultValue: moment.duration(1, 'day').asMilliseconds(),
}),
// The frequency which each repo tries to update. 5 minutes by default.
updateRepoFrequencyMs: schema.number({
defaultValue: moment.duration(5, 'minute').asMilliseconds(),
}),
// The frequency which each repo tries to index. 1 day by default.
indexRepoFrequencyMs: schema.number({
defaultValue: moment.duration(1, 'day').asMilliseconds(),
}),
// whether we want to show more logs
verbose: schema.boolean({ defaultValue: false }),
lsp: schema.object({
...langSwitches,
// timeout of a request
requestTimeoutMs: schema.number({
defaultValue: moment.duration(10, 'second').asMilliseconds(),
}),
// if we want the language server run in seperately
detach: schema.boolean({ defaultValue: false }),
// enable oom_score_adj on linux
oomScoreAdj: schema.boolean({ defaultValue: true }),
}),
repos: schema.arrayOf(schema.string(), { defaultValue: [] }),
security: schema.object({
enableMavenImport: schema.boolean({ defaultValue: true }),
enableGradleImport: schema.boolean({ defaultValue: false }),
installGoDependency: schema.boolean({ defaultValue: false }),
installNodeDependency: schema.boolean({ defaultValue: true }),
gitHostWhitelist: schema.arrayOf(schema.string(), {
defaultValue: [
'github.com',
'gitlab.com',
'bitbucket.org',
'gitbox.apache.org',
'eclipse.org',
],
}),
gitProtocolWhitelist: schema.arrayOf(schema.string(), {
defaultValue: ['https', 'git', 'ssh'],
}),
enableGitCertCheck: schema.boolean({ defaultValue: true }),
}),
disk: schema.object({
thresholdEnabled: schema.boolean({ defaultValue: true }),
watermarkLow: schema.string({ defaultValue: `${DEFAULT_WATERMARK_LOW_PERCENTAGE}%` }),
}),
maxWorkspace: schema.number({ defaultValue: 5 }), // max workspace folder for each language server
enableGlobalReference: schema.boolean({ defaultValue: false }), // Global reference as optional feature for now
enableCommitIndexing: schema.boolean({ defaultValue: false }),
codeNodeUrl: schema.maybe(schema.string()),
clustering: schema.object({
enabled: schema.boolean({ defaultValue: false }),
codeNodes: schema.arrayOf(
schema.object({
id: schema.string(),
address: schema.string(),
}),
{
defaultValue: [],
}
),
}),
});
};
export const CodeConfigSchema = createCodeConfigSchema();

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;
* you may not use this file except in compliance with the Elastic License.
*/
import { PluginInitializerContext } from 'src/core/server';
import { CodeConfigSchema } from './config';
import { CodePlugin } from './plugin';
export { PluginSetupContract } from './plugin';
export const config = { schema: CodeConfigSchema };
export const plugin = (initializerContext: PluginInitializerContext) =>
new CodePlugin(initializerContext);

View file

@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { first } from 'rxjs/operators';
import { TypeOf } from '@kbn/config-schema';
import {
CoreSetup,
LoggerFactory,
PluginInitializerContext,
RecursiveReadonly,
} from 'src/core/server';
import { deepFreeze } from '../../../../src/core/utils';
import { PluginSetupContract as FeaturesSetupContract } from '../../features/server';
import { CodeConfigSchema } from './config';
/**
* Describes public Code plugin contract returned at the `setup` stage.
*/
export interface PluginSetupContract {
/** @deprecated */
legacy: {
config: TypeOf<typeof CodeConfigSchema>;
logger: LoggerFactory;
};
}
/**
* Represents Code Plugin instance that will be managed by the Kibana plugin system.
*/
export class CodePlugin {
constructor(private readonly initializerContext: PluginInitializerContext) {}
public async setup(
coreSetup: CoreSetup,
{ features }: { features: FeaturesSetupContract }
): Promise<RecursiveReadonly<PluginSetupContract>> {
const config = await this.initializerContext.config
.create<TypeOf<typeof CodeConfigSchema>>()
.pipe(first())
.toPromise();
features.registerFeature({
id: 'code',
name: i18n.translate('xpack.code.featureRegistry.codeFeatureName', {
defaultMessage: 'Code',
}),
icon: 'codeApp',
navLinkId: 'code',
app: ['code', 'kibana'],
catalogue: [], // TODO add catalogue here
privileges: {
all: {
excludeFromBasePrivileges: true,
api: ['code_user', 'code_admin'],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show', 'user', 'admin'],
},
read: {
api: ['code_user'],
savedObject: {
all: [],
read: ['config'],
},
ui: ['show', 'user'],
},
},
});
return deepFreeze({
/** @deprecated */
legacy: {
config,
logger: this.initializerContext.logger,
},
});
}
public start() {
this.initializerContext.logger.get().debug('Starting Code plugin');
}
public stop() {
this.initializerContext.logger.get().debug('Stopping Code plugin');
}
}