-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EMT-146: use ingest agent for status info #63921
Changes from 9 commits
ad9dad3
c653765
eb13637
110efc6
e29c087
4e53a4f
ba532d0
179d09e
229df97
578e0bf
bcd6e4c
b36c29f
f591e86
62eba60
e990c47
2bd6601
6d4c1ed
9bb3550
f7d0bad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,18 +4,29 @@ | |
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
import { IRouter, RequestHandlerContext } from 'kibana/server'; | ||
import { IRouter, Logger, RequestHandlerContext } from 'kibana/server'; | ||
import { SearchResponse } from 'elasticsearch'; | ||
import { schema } from '@kbn/config-schema'; | ||
|
||
import { kibanaRequestToMetadataListESQuery, getESQueryHostMetadataByID } from './query_builders'; | ||
import { getESQueryHostMetadataByID, kibanaRequestToMetadataListESQuery } from './query_builders'; | ||
import { HostInfo, HostMetadata, HostResultList, HostStatus } from '../../../common/types'; | ||
import { EndpointAppContext } from '../../types'; | ||
import { AgentStatus } from '../../../../ingest_manager/common/types/models'; | ||
|
||
interface HitSource { | ||
_source: HostMetadata; | ||
} | ||
|
||
interface MetadataRequestContext { | ||
requestHandlerContext: RequestHandlerContext; | ||
endpointAppContext: EndpointAppContext; | ||
} | ||
|
||
const HOST_STATUS_MAPPING = new Map<AgentStatus, HostStatus>([ | ||
['online', HostStatus.ONLINE], | ||
['offline', HostStatus.OFFLINE], | ||
]); | ||
|
||
export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { | ||
router.post( | ||
{ | ||
|
@@ -62,7 +73,12 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp | |
'search', | ||
queryParams | ||
)) as SearchResponse<HostMetadata>; | ||
return res.ok({ body: mapToHostResultList(queryParams, response) }); | ||
return res.ok({ | ||
body: await mapToHostResultList(queryParams, response, { | ||
endpointAppContext, | ||
requestHandlerContext: context, | ||
}), | ||
}); | ||
} catch (err) { | ||
return res.internalError({ body: err }); | ||
} | ||
|
@@ -79,11 +95,13 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp | |
}, | ||
async (context, req, res) => { | ||
try { | ||
const index = await endpointAppContext.indexPatternRetriever.getMetadataIndexPattern( | ||
context | ||
const doc = await getHostData( | ||
{ | ||
endpointAppContext, | ||
requestHandlerContext: context, | ||
}, | ||
req.params.id | ||
); | ||
|
||
const doc = await getHostData(context, req.params.id, index); | ||
if (doc) { | ||
return res.ok({ body: doc }); | ||
} | ||
|
@@ -96,12 +114,14 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp | |
} | ||
|
||
export async function getHostData( | ||
context: RequestHandlerContext, | ||
id: string, | ||
index: string | ||
metadataRequestContext: MetadataRequestContext, | ||
id: string | ||
): Promise<HostInfo | undefined> { | ||
const index = await metadataRequestContext.endpointAppContext.indexPatternRetriever.getMetadataIndexPattern( | ||
metadataRequestContext.requestHandlerContext | ||
); | ||
const query = getESQueryHostMetadataByID(id, index); | ||
const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( | ||
const response = (await metadataRequestContext.requestHandlerContext.core.elasticsearch.dataClient.callAsCurrentUser( | ||
'search', | ||
query | ||
)) as SearchResponse<HostMetadata>; | ||
|
@@ -110,22 +130,25 @@ export async function getHostData( | |
return undefined; | ||
} | ||
|
||
return enrichHostMetadata(response.hits.hits[0]._source); | ||
return await enrichHostMetadata(response.hits.hits[0]._source, metadataRequestContext); | ||
} | ||
|
||
function mapToHostResultList( | ||
async function mapToHostResultList( | ||
queryParams: Record<string, any>, | ||
searchResponse: SearchResponse<HostMetadata> | ||
): HostResultList { | ||
searchResponse: SearchResponse<HostMetadata>, | ||
metadataRequestContext: MetadataRequestContext | ||
): Promise<HostResultList> { | ||
const totalNumberOfHosts = searchResponse?.aggregations?.total?.value || 0; | ||
if (searchResponse.hits.hits.length > 0) { | ||
return { | ||
request_page_size: queryParams.size, | ||
request_page_index: queryParams.from, | ||
hosts: searchResponse.hits.hits | ||
.map(response => response.inner_hits.most_recent.hits.hits) | ||
.flatMap(data => data as HitSource) | ||
.map(entry => enrichHostMetadata(entry._source)), | ||
hosts: await Promise.all( | ||
searchResponse.hits.hits | ||
.map(response => response.inner_hits.most_recent.hits.hits) | ||
.flatMap(data => data as HitSource) | ||
.map(async entry => enrichHostMetadata(entry._source, metadataRequestContext)) | ||
), | ||
total: totalNumberOfHosts, | ||
}; | ||
} else { | ||
|
@@ -138,9 +161,32 @@ function mapToHostResultList( | |
} | ||
} | ||
|
||
function enrichHostMetadata(hostMetadata: HostMetadata): HostInfo { | ||
async function enrichHostMetadata( | ||
hostMetadata: HostMetadata, | ||
metadataRequestContext: MetadataRequestContext | ||
): Promise<HostInfo> { | ||
let hostStatus = HostStatus.ERROR; | ||
try { | ||
const status = await metadataRequestContext.endpointAppContext.agentService.getAgentStatus( | ||
metadataRequestContext.requestHandlerContext, | ||
hostMetadata.elastic.agent.id | ||
); | ||
hostStatus = HOST_STATUS_MAPPING.get(status) || HostStatus.ERROR; | ||
} catch (e) { | ||
if (e.isBoom && e.output.statusCode === 404) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the ingest api throw a boom? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if a saved object is not found is going throw a Boom error There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, |
||
logger(metadataRequestContext.endpointAppContext).warn( | ||
`agent with id ${hostMetadata.elastic.agent.id} not found` | ||
); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
return { | ||
metadata: hostMetadata, | ||
host_status: HostStatus.ERROR, | ||
host_status: hostStatus, | ||
}; | ||
} | ||
|
||
const logger = (endpointAppContext: EndpointAppContext): Logger => { | ||
return endpointAppContext.logFactory.get('metadata'); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I won't be able to try this locally until Monday afternoon, but I think this will work. If so, we can avoid the new
getAgentStatus
this PR adds in Ingest Manager.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfsiii Can we talk about this. I would think a wholesale service will be better that performs the functionality will be better. And this does not isolate from changes to how agent status is derived. With this approach we have too much knowledge of the internal working of Ingest. This will mean will do a wholesale export of the entire services directory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nnamdifrankie sure, we can talk anytime. I'm available most days from 1pm-5pm ET.
Before then, I'll try to summary my understanding & suggestions.
As I understand it, this PR adds an Agent Service but Ingest Manager already has one
It also adds a function
getAgentStatus
which a) is a duplicate of an existing function and b) uses the existing services functions from Ingest.I'd like to avoid both those issues and think we can do that by moving that function into
services/agents/status
, changing its name to something likegetAgentStatusById
, and have it accept a saved object client and string id. That's in line with lots of our other service functions.That way, all you're really changing at this call site is the function name and the first argument. It's still only calling one function and Ingest is the one in control of that logic.
Hope that helps. Reach out any time to talk more.