Skip to content

Commit

Permalink
Make ingestManager optional for security-solution startup
Browse files Browse the repository at this point in the history
  • Loading branch information
madirey committed Jul 6, 2020
1 parent 13d9ec8 commit feb1202
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 65 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"embeddable",
"features",
"home",
"ingestManager",
"taskManager",
"inspector",
"licensing",
Expand All @@ -21,6 +20,7 @@
],
"optionalPlugins": [
"encryptedSavedObjects",
"ingestManager",
"ml",
"newsfeed",
"security",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import { httpServerMock } from '../../../../../src/core/server/mocks';
import { EndpointAppContextService } from './endpoint_app_context_services';

describe('test endpoint app context services', () => {
it('should throw error on getAgentService if start is not called', async () => {
it('should return undefined on getAgentService if dependencies are not enabled', async () => {
const endpointAppContextService = new EndpointAppContextService();
expect(() => endpointAppContextService.getAgentService()).toThrow(Error);
});
it('should return undefined on getManifestManager if start is not called', async () => {
it('should return undefined on getManifestManager if dependencies are not enabled', async () => {
const endpointAppContextService = new EndpointAppContextService();
expect(endpointAppContextService.getManifestManager()).toEqual(undefined);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import { AgentService, IngestManagerStartContract } from '../../../ingest_manage
import { getPackageConfigCreateCallback } from './ingest_integration';
import { ManifestManager } from './services/artifacts';

export type EndpointAppContextServiceStartContract = Pick<
IngestManagerStartContract,
'agentService'
export type EndpointAppContextServiceStartContract = Partial<
Pick<IngestManagerStartContract, 'agentService'>
> & {
manifestManager?: ManifestManager | undefined;
registerIngestCallback: IngestManagerStartContract['registerExternalCallback'];
manifestManager?: ManifestManager;
registerIngestCallback?: IngestManagerStartContract['registerExternalCallback'];
savedObjectsStart: SavedObjectsServiceStart;
};

Expand All @@ -35,7 +34,7 @@ export class EndpointAppContextService {
this.manifestManager = dependencies.manifestManager;
this.savedObjectsStart = dependencies.savedObjectsStart;

if (this.manifestManager !== undefined) {
if (this.manifestManager && dependencies.registerIngestCallback) {
dependencies.registerIngestCallback(
'packageConfigCreate',
getPackageConfigCreateCallback(this.manifestManager)
Expand All @@ -45,10 +44,7 @@ export class EndpointAppContextService {

public stop() {}

public getAgentService(): AgentService {
if (!this.agentService) {
throw new Error(`must call start on ${EndpointAppContextService.name} to call getter`);
}
public getAgentService(): AgentService | undefined {
return this.agentService;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
HostStatus,
} from '../../../../common/endpoint/types';
import { EndpointAppContext } from '../../types';
import { AgentService } from '../../../../../ingest_manager/server';
import { Agent, AgentStatus } from '../../../../../ingest_manager/common/types/models';
import { findAllUnenrolledAgentIds } from './support/unenroll';

Expand All @@ -26,17 +27,22 @@ interface HitSource {
}

interface MetadataRequestContext {
agentService: AgentService;
logger: Logger;
requestHandlerContext: RequestHandlerContext;
endpointAppContext: EndpointAppContext;
}

const HOST_STATUS_MAPPING = new Map<AgentStatus, HostStatus>([
['online', HostStatus.ONLINE],
['offline', HostStatus.OFFLINE],
]);

const getLogger = (endpointAppContext: EndpointAppContext): Logger => {
return endpointAppContext.logFactory.get('metadata');
};

export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) {
const logger = endpointAppContext.logFactory.get('metadata');
const logger = getLogger(endpointAppContext);
router.post(
{
path: '/api/endpoint/metadata',
Expand Down Expand Up @@ -66,12 +72,23 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
})
),
},
options: { authRequired: true },
options: { authRequired: true, tags: ['access:securitySolution'] },
},
async (context, req, res) => {
const agentService = endpointAppContext.service.getAgentService();
if (agentService === undefined) {
return res.internalError({ body: 'agentService not available' });
}

const metadataRequestContext: MetadataRequestContext = {
agentService,
logger,
requestHandlerContext: context,
};

try {
const unenrolledAgentIds = await findAllUnenrolledAgentIds(
endpointAppContext.service.getAgentService(),
agentService,
context.core.savedObjects.client
);

Expand All @@ -89,10 +106,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
queryParams
)) as SearchResponse<HostMetadata>;
return res.ok({
body: await mapToHostResultList(queryParams, response, {
endpointAppContext,
requestHandlerContext: context,
}),
body: await mapToHostResultList(queryParams, response, metadataRequestContext),
});
} catch (err) {
logger.warn(JSON.stringify(err, null, 2));
Expand All @@ -107,17 +121,16 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
validate: {
params: schema.object({ id: schema.string() }),
},
options: { authRequired: true },
options: { authRequired: true, tags: ['access:securitySolution'] },
},
async (context, req, res) => {
const agentService = endpointAppContext.service.getAgentService();
if (agentService === undefined) {
return res.internalError({ body: 'agentService not available' });
}

try {
const doc = await getHostData(
{
endpointAppContext,
requestHandlerContext: context,
},
req.params.id
);
const doc = await getHostData(context, agentService, req.params.id);
if (doc) {
return res.ok({ body: doc });
}
Expand Down Expand Up @@ -164,17 +177,16 @@ async function findAgent(
metadataRequestContext: MetadataRequestContext,
hostMetadata: HostMetadata
): Promise<Agent | undefined> {
const logger = metadataRequestContext.endpointAppContext.logFactory.get('metadata');
try {
return await metadataRequestContext.endpointAppContext.service
.getAgentService()
.getAgent(
metadataRequestContext.requestHandlerContext.core.savedObjects.client,
hostMetadata.elastic.agent.id
);
return await metadataRequestContext.agentService.getAgent(
metadataRequestContext.requestHandlerContext.core.savedObjects.client,
hostMetadata.elastic.agent.id
);
} catch (e) {
if (e.isBoom && e.output.statusCode === 404) {
logger.warn(`agent with id ${hostMetadata.elastic.agent.id} not found`);
metadataRequestContext.logger.warn(
`agent with id ${hostMetadata.elastic.agent.id} not found`
);
return undefined;
} else {
throw e;
Expand Down Expand Up @@ -217,7 +229,7 @@ async function enrichHostMetadata(
): Promise<HostInfo> {
let hostStatus = HostStatus.ERROR;
let elasticAgentId = hostMetadata?.elastic?.agent?.id;
const log = logger(metadataRequestContext.endpointAppContext);
const log = metadataRequestContext.logger;
try {
/**
* Get agent status by elastic agent id if available or use the host id.
Expand All @@ -228,12 +240,10 @@ async function enrichHostMetadata(
log.warn(`Missing elastic agent id, using host id instead ${elasticAgentId}`);
}

const status = await metadataRequestContext.endpointAppContext.service
.getAgentService()
.getAgentStatusById(
metadataRequestContext.requestHandlerContext.core.savedObjects.client,
elasticAgentId
);
const status = await metadataRequestContext.agentService.getAgentStatusById(
metadataRequestContext.requestHandlerContext.core.savedObjects.client,
elasticAgentId
);
hostStatus = HOST_STATUS_MAPPING.get(status) || HostStatus.ERROR;
} catch (e) {
if (e.isBoom && e.output.statusCode === 404) {
Expand All @@ -248,7 +258,3 @@ async function enrichHostMetadata(
host_status: hostStatus,
};
}

const logger = (endpointAppContext: EndpointAppContext): Logger => {
return endpointAppContext.logFactory.get('metadata');
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ describe('test endpoint route', () => {
let routeHandler: RequestHandler<any, any, any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let routeConfig: RouteConfig<any, any, any, any>;
let mockAgentService: ReturnType<
typeof createMockEndpointAppContextServiceStartContract
// tests assume that ingestManager is enabled, and thus agentService is available
let mockAgentService: Required<
ReturnType<typeof createMockEndpointAppContextServiceStartContract>
>['agentService'];
let endpointAppContextService: EndpointAppContextService;
const noUnenrolledAgent = {
Expand All @@ -70,7 +71,7 @@ describe('test endpoint route', () => {
endpointAppContextService = new EndpointAppContextService();
const startContract = createMockEndpointAppContextServiceStartContract();
endpointAppContextService.start(startContract);
mockAgentService = startContract.agentService;
mockAgentService = startContract.agentService!;

registerEndpointRoutes(routerMock, {
logFactory: loggingSystemMock.create(),
Expand Down
40 changes: 26 additions & 14 deletions x-pack/plugins/security_solution/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { ListPluginSetup } from '../../lists/server';
import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server';
import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server';
import { LicensingPluginSetup } from '../../licensing/server';
import { IngestManagerStartContract } from '../../ingest_manager/server';
import { IngestManagerStartContract, ExternalCallback } from '../../ingest_manager/server';
import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server';
import { initServer } from './init_server';
import { compose } from './lib/compose/kibana';
Expand Down Expand Up @@ -55,14 +55,14 @@ export interface SetupPlugins {
licensing: LicensingPluginSetup;
security?: SecuritySetup;
spaces?: SpacesSetup;
taskManager: TaskManagerSetupContract;
taskManager?: TaskManagerSetupContract;
ml?: MlSetup;
lists?: ListPluginSetup;
}

export interface StartPlugins {
ingestManager: IngestManagerStartContract;
taskManager: TaskManagerStartContract;
ingestManager?: IngestManagerStartContract;
taskManager?: TaskManagerStartContract;
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down Expand Up @@ -224,11 +224,15 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
}
}

if (plugins.taskManager && plugins.lists) {
const exceptionListsSetupEnabled = () => {
return plugins.taskManager && plugins.lists;
};

if (exceptionListsSetupEnabled()) {
this.lists = plugins.lists;
this.manifestTask = new ManifestTask({
endpointAppContext: endpointContext,
taskManager: plugins.taskManager,
taskManager: plugins.taskManager!,
});
}

Expand All @@ -242,32 +246,40 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
const savedObjectsClient = new SavedObjectsClient(core.savedObjects.createInternalRepository());

let manifestManager: ManifestManager | undefined;
if (this.lists) {
const exceptionListClient = this.lists.getExceptionListClient(savedObjectsClient, 'kibana');
let registerIngestCallback: ((...args: ExternalCallback) => void) | undefined;

const exceptionListsStartEnabled = () => {
return this.lists && plugins.taskManager && plugins.ingestManager;
};

if (exceptionListsStartEnabled()) {
const exceptionListClient = this.lists!.getExceptionListClient(savedObjectsClient, 'kibana');
const artifactClient = new ArtifactClient(savedObjectsClient);

registerIngestCallback = plugins.ingestManager!.registerExternalCallback;
manifestManager = new ManifestManager({
savedObjectsClient,
artifactClient,
exceptionListClient,
packageConfigService: plugins.ingestManager.packageConfigService,
packageConfigService: plugins.ingestManager!.packageConfigService,
logger: this.logger,
cache: this.exceptionsCache,
});
}

this.endpointAppContextService.start({
agentService: plugins.ingestManager.agentService,
agentService: plugins.ingestManager?.agentService,
manifestManager,
registerIngestCallback: plugins.ingestManager.registerExternalCallback,
registerIngestCallback,
savedObjectsStart: core.savedObjects,
});

if (this.manifestTask) {
if (exceptionListsStartEnabled() && this.manifestTask) {
this.manifestTask.start({
taskManager: plugins.taskManager,
taskManager: plugins.taskManager!,
});
} else {
this.logger.debug('Manifest task not available.');
this.logger.debug('User artifacts task not available.');
}

return {};
Expand Down

0 comments on commit feb1202

Please sign in to comment.