Skip to content

Commit

Permalink
[Endpoint] EMT-146: add host status info to the metadata API response (
Browse files Browse the repository at this point in the history
…#62876)

[Endpoint] EMT-146: add host status info to the metadata API response
  • Loading branch information
nnamdifrankie authored Apr 8, 2020
1 parent 2023663 commit 3457dde
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 26 deletions.
28 changes: 27 additions & 1 deletion x-pack/plugins/endpoint/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export interface AlertResultList {

export interface HostResultList {
/* the hosts restricted by the page size */
hosts: HostMetadata[];
hosts: HostInfo[];
/* the total number of unique hosts in the index */
total: number;
/* the page size requested */
Expand Down Expand Up @@ -252,6 +252,32 @@ export type AlertData = AlertEvent & AlertMetadata;

export type AlertDetails = AlertData & AlertState;

/**
* The status of the host
*/
export enum HostStatus {
/**
* Default state of the host when no host information is present or host information cannot
* be retrieved. e.g. API error
*/
ERROR = 'error',

/**
* Host is online as indicated by its checkin status during the last checkin window
*/
ONLINE = 'online',

/**
* Host is offline as indicated by its checkin status during the last checkin window
*/
OFFLINE = 'offline',
}

export type HostInfo = Immutable<{
metadata: HostMetadata;
host_status: HostStatus;
}>;

export type HostMetadata = Immutable<{
'@timestamp': number;
event: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { HostListPagination, ServerApiError } from '../../types';
import { HostResultList, HostMetadata } from '../../../../../common/types';
import { HostResultList, HostInfo } from '../../../../../common/types';

interface ServerReturnedHostList {
type: 'serverReturnedHostList';
Expand All @@ -14,7 +14,7 @@ interface ServerReturnedHostList {

interface ServerReturnedHostDetails {
type: 'serverReturnedHostDetails';
payload: HostMetadata;
payload: HostInfo;
}

interface ServerFailedToReturnHostDetails {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('HostList store concerns', () => {
});

const currentState = store.getState();
expect(currentState.hosts).toEqual(payload.hosts);
expect(currentState.hosts).toEqual(payload.hosts.map(hostInfo => hostInfo.metadata));
expect(currentState.pageSize).toEqual(payload.request_page_size);
expect(currentState.pageIndex).toEqual(payload.request_page_index);
expect(currentState.total).toEqual(payload.total);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ describe('host list middleware', () => {
paging_properties: [{ page_index: 0 }, { page_size: 10 }],
}),
});
expect(listData(getState())).toEqual(apiResponse.hosts);
expect(listData(getState())).toEqual(apiResponse.hosts.map(hostInfo => hostInfo.metadata));
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { HostResultList } from '../../../../../common/types';
import { HostResultList, HostStatus } from '../../../../../common/types';
import { EndpointDocGenerator } from '../../../../../common/generate_data';

export const mockHostResultList: (options?: {
Expand All @@ -27,7 +27,10 @@ export const mockHostResultList: (options?: {
const hosts = [];
for (let index = 0; index < actualCountToReturn; index++) {
const generator = new EndpointDocGenerator('seed');
hosts.push(generator.generateHostMetadata());
hosts.push({
metadata: generator.generateHostMetadata(),
host_status: HostStatus.ERROR,
});
}
const mock: HostResultList = {
hosts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const hostListReducer: Reducer<HostListState, AppAction> = (
} = action.payload;
return {
...state,
hosts,
hosts: hosts.map(hostInfo => hostInfo.metadata),
total,
pageSize,
pageIndex,
Expand All @@ -43,7 +43,7 @@ export const hostListReducer: Reducer<HostListState, AppAction> = (
} else if (action.type === 'serverReturnedHostDetails') {
return {
...state,
details: action.payload,
details: action.payload.metadata,
};
} else if (action.type === 'serverFailedToReturnHostDetails') {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const alertDetailsHandlerWrapper = function(
id: response._id,
...response._source,
state: {
host_metadata: currentHostInfo,
host_metadata: currentHostInfo?.metadata,
},
next: await pagination.getNextUrl(),
prev: await pagination.getPrevUrl(),
Expand Down
15 changes: 11 additions & 4 deletions x-pack/plugins/endpoint/server/routes/metadata/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IRouter, RequestHandlerContext } from 'kibana/server';
import { SearchResponse } from 'elasticsearch';
import { schema } from '@kbn/config-schema';

import { HostMetadata, HostResultList } from '../../../common/types';
import { HostInfo, HostMetadata, HostResultList, HostStatus } from '../../../common/types';
import { EndpointAppContext } from '../../types';
import { getESQueryHostMetadataByID, kibanaRequestToMetadataListESQuery } from './query_builders';

Expand Down Expand Up @@ -87,7 +87,7 @@ export function registerEndpointRoutes(router: IRouter, endpointAppContext: Endp
export async function getHostData(
context: RequestHandlerContext,
id: string
): Promise<HostMetadata | undefined> {
): Promise<HostInfo | undefined> {
const query = getESQueryHostMetadataByID(id);
const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser(
'search',
Expand All @@ -98,7 +98,7 @@ export async function getHostData(
return undefined;
}

return response.hits.hits[0]._source;
return enrichHostMetadata(response.hits.hits[0]._source);
}

function mapToHostResultList(
Expand All @@ -113,7 +113,7 @@ function mapToHostResultList(
hosts: searchResponse.hits.hits
.map(response => response.inner_hits.most_recent.hits.hits)
.flatMap(data => data as HitSource)
.map(entry => entry._source),
.map(entry => enrichHostMetadata(entry._source)),
total: totalNumberOfHosts,
};
} else {
Expand All @@ -125,3 +125,10 @@ function mapToHostResultList(
};
}
}

function enrichHostMetadata(hostMetadata: HostMetadata): HostInfo {
return {
metadata: hostMetadata,
host_status: HostStatus.ERROR,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
httpServiceMock,
loggingServiceMock,
} from '../../../../../../src/core/server/mocks';
import { HostMetadata, HostResultList } from '../../../common/types';
import { HostInfo, HostMetadata, HostResultList, HostStatus } from '../../../common/types';
import { SearchResponse } from 'elasticsearch';
import { EndpointConfigSchema } from '../../config';
import * as data from '../../test_data/all_metadata_data.json';
Expand Down Expand Up @@ -230,7 +230,7 @@ describe('test endpoint route', () => {
expect(message).toEqual('Endpoint Not Found');
});

it('should return a single endpoint', async () => {
it('should return a single endpoint with status error', async () => {
const mockRequest = httpServerMock.createKibanaRequest({
params: { id: (data as any).hits.hits[0]._id },
});
Expand All @@ -257,8 +257,9 @@ describe('test endpoint route', () => {
expect(mockScopedClient.callAsCurrentUser).toBeCalled();
expect(routeConfig.options).toEqual({ authRequired: true });
expect(mockResponse.ok).toBeCalled();
const result = mockResponse.ok.mock.calls[0][0]?.body as HostMetadata;
expect(result).toHaveProperty('endpoint');
const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo;
expect(result).toHaveProperty('metadata.endpoint');
expect(result.host_status).toEqual(HostStatus.ERROR);
});
});
});
17 changes: 9 additions & 8 deletions x-pack/test/api_integration/apis/endpoint/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export default function({ getService }: FtrProviderContext) {
.expect(200);
expect(body.total).to.eql(2);
const resultIps: string[] = [].concat(
...body.hosts.map((metadata: Record<string, any>) => metadata.host.ip)
...body.hosts.map((hostInfo: Record<string, any>) => hostInfo.metadata.host.ip)
);
expect(resultIps).to.eql([
'10.192.213.130',
Expand All @@ -164,7 +164,7 @@ export default function({ getService }: FtrProviderContext) {
.expect(200);
expect(body.total).to.eql(2);
const resultOsVariantValue: Set<string> = new Set(
body.hosts.map((metadata: Record<string, any>) => metadata.host.os.variant)
body.hosts.map((hostInfo: Record<string, any>) => hostInfo.metadata.host.os.variant)
);
expect(Array.from(resultOsVariantValue)).to.eql([variantValue]);
expect(body.hosts.length).to.eql(2);
Expand All @@ -182,17 +182,17 @@ export default function({ getService }: FtrProviderContext) {
})
.expect(200);
expect(body.total).to.eql(1);
const resultIp: string = body.hosts[0].host.ip.filter(
const resultIp: string = body.hosts[0].metadata.host.ip.filter(
(ip: string) => ip === targetEndpointIp
);
expect(resultIp).to.eql([targetEndpointIp]);
expect(body.hosts[0].event.created).to.eql(1579881969541);
expect(body.hosts[0].metadata.event.created).to.eql(1579881969541);
expect(body.hosts.length).to.eql(1);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
});

it('metadata api should return the endpoint based on the elastic agent id', async () => {
it('metadata api should return the endpoint based on the elastic agent id, and status should be error', async () => {
const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf';
const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095';
const { body } = await supertest
Expand All @@ -203,11 +203,12 @@ export default function({ getService }: FtrProviderContext) {
})
.expect(200);
expect(body.total).to.eql(1);
const resultHostId: string = body.hosts[0].host.id;
const resultElasticAgentId: string = body.hosts[0].elastic.agent.id;
const resultHostId: string = body.hosts[0].metadata.host.id;
const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id;
expect(resultHostId).to.eql(targetEndpointId);
expect(resultElasticAgentId).to.eql(targetElasticAgentId);
expect(body.hosts[0].event.created).to.eql(1579881969541);
expect(body.hosts[0].metadata.event.created).to.eql(1579881969541);
expect(body.hosts[0].host_status).to.eql('error');
expect(body.hosts.length).to.eql(1);
expect(body.request_page_size).to.eql(10);
expect(body.request_page_index).to.eql(0);
Expand Down

0 comments on commit 3457dde

Please sign in to comment.