Skip to content

Commit

Permalink
WIP: added a few tests
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelolo24 committed Jul 9, 2020
1 parent 6aa74a1 commit 9247b6a
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 64 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/*
* 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 { AgentEventSOAttributes } from './../../../../ingest_manager/common/types/models/agent';
import {
AGENT_SAVED_OBJECT_TYPE,
AGENT_EVENT_SAVED_OBJECT_TYPE,
} from './../../../../ingest_manager/common/constants/agent';
import { Agent } from '../../../../ingest_manager/common';
import { savedObjectsClientMock } from 'src/core/server/mocks';
import { SavedObjectsClientContract, SavedObjectsFindResponse } from 'src/core/server';
import * as endpointTelemetry from './endpoint_metadata_telemetry';
import * as endpointTelemetrySavedObjects from './endpoint_telemetry_saved_objects';

const testAgentId = 'testAgentId';
const testConfigId = 'testConfigId';
const osPlatform = 'somePlatform';
const osName = 'somePlatformName';
const osFullName = 'somePlatformFullName';
const osVersion = '1';

const mockFleetObjectsResponse = (
lastCheckIn = new Date().toISOString()
): SavedObjectsFindResponse<Agent> => ({
page: 1,
per_page: 20,
total: 1,
saved_objects: [
{
type: AGENT_SAVED_OBJECT_TYPE,
id: testAgentId,
attributes: {
active: true,
id: testAgentId,
config_id: 'randoConfigId',
type: 'PERMANENT',
user_provided_metadata: {},
enrolled_at: '2020-07-08T20:07:44.083Z',
current_error_events: [],
local_metadata: {
elastic: {
agent: {
id: testAgentId,
},
},
host: {
hostname: 'testDesktop',
name: 'testDesktop',
id: 'randoHostId',
},
os: {
platform: osPlatform,
version: osVersion,
name: osName,
full: osFullName,
},
},
packages: [endpointTelemetrySavedObjects.ENDPOINT_PACKAGE_CONSTANT, 'system'],
last_checkin: lastCheckIn,
},
references: [],
updated_at: '2020-07-08T20:55:09.216Z',
version: 'WzI4MSwxXQ==',
score: 0,
},
],
});

const mockFleetEventsObjectsResponse = (
running?: boolean,
updatedDate = new Date().toISOString()
): SavedObjectsFindResponse<AgentEventSOAttributes> => {
return {
page: 1,
per_page: 20,
total: 2,
saved_objects: [
{
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
id: 'id1',
attributes: {
agent_id: testAgentId,
type: running ? 'STATE' : 'ERROR',
timestamp: updatedDate,
subtype: running ? 'RUNNING' : 'FAILED',
message: `Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to ${
running ? 'RUNNING' : 'FAILED'
}: `,
config_id: testConfigId,
},
references: [],
updated_at: updatedDate,
version: 'WzExOCwxXQ==',
score: 0,
},
{
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
id: 'id2',
attributes: {
agent_id: testAgentId,
type: 'STATE',
timestamp: updatedDate,
subtype: 'STARTING',
message:
'Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to STARTING: Starting',
config_id: testConfigId,
},
references: [],
updated_at: updatedDate,
version: 'WzExNywxXQ==',
score: 0,
},
],
};
};

describe('test security solution endpoint telemetry', () => {
let mockSavedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
let getFleetSavedObjectsMetadataSpy;
let getFleetEventsSavedObjectsSpy;

beforeAll(() => {
getFleetEventsSavedObjectsSpy = jest.spyOn(
endpointTelemetrySavedObjects,
'getFleetEventsSavedObjects'
);
getFleetSavedObjectsMetadataSpy = jest.spyOn(
endpointTelemetrySavedObjects,
'getFleetSavedObjectsMetadata'
);
mockSavedObjectsClient = savedObjectsClientMock.create();
});

afterAll(() => {
jest.resetAllMocks();
});

it('should have a default shape', () => {
expect(endpointTelemetry.getDefaultEndpointTelemetry()).toMatchInlineSnapshot(`
Object {
"active_within_last_24_hours": 0,
"os": Object {},
"total_installed": 0,
}
`);
});

describe('when agents have not been installed', () => {
it('should return the default shape if no agents are found', async () => {
getFleetSavedObjectsMetadataSpy.mockImplementation(() =>
Promise.resolve({ saved_objects: [], total: 0, per_page: 0, page: 0 })
);

const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointMetadataTelemetryFromFleet(
mockSavedObjectsClient
);
expect(getFleetSavedObjectsMetadataSpy).toHaveBeenCalled();
expect(emptyEndpointTelemetryData).toEqual({
total_installed: 0,
active_within_last_24_hours: 0,
os: {},
});
});
});

describe('when agents have been installed', () => {
let withinLastDay;
let olderThanADay;
beforeEach(() => {
const currentTime = new Date();

withinLastDay = new Date();
withinLastDay.setHours(currentTime.getHours() - 10);
withinLastDay = withinLastDay.toISOString();

olderThanADay = new Date();
olderThanADay.setDate(currentTime.getDate() - 2);
olderThanADay = olderThanADay.toISOString();
});

it('should show one installed but no active endpoint', async () => {
getFleetSavedObjectsMetadataSpy.mockImplementation(() =>
Promise.resolve(mockFleetObjectsResponse())
);
getFleetEventsSavedObjectsSpy.mockImplementation(() =>
Promise.resolve(mockFleetEventsObjectsResponse())
);

const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointMetadataTelemetryFromFleet(
mockSavedObjectsClient
);
expect(emptyEndpointTelemetryData).toEqual({
total_installed: 1,
active_within_last_24_hours: 0,
os: {
[osName]: {
name: osFullName,
version: osVersion,
count: 1,
},
},
});
});

it('should show one installed with an active endpoint', async () => {
getFleetSavedObjectsMetadataSpy.mockImplementation(() =>
Promise.resolve(mockFleetObjectsResponse())
);
getFleetEventsSavedObjectsSpy.mockImplementation(() =>
Promise.resolve(mockFleetEventsObjectsResponse(true))
);

const emptyEndpointTelemetryData = await endpointTelemetry.getEndpointMetadataTelemetryFromFleet(
mockSavedObjectsClient
);
expect(emptyEndpointTelemetryData).toEqual({
total_installed: 1,
active_within_last_24_hours: 1,
os: {
[osName]: {
name: osFullName,
version: osVersion,
count: 1,
},
},
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { ISavedObjectsRepository } from 'src/core/server';
import { SavedObjectsClientContract } from 'src/core/server';
import { AgentMetadata } from './../../../../ingest_manager/common/types/models/agent';
import { AGENT_SAVED_OBJECT_TYPE } from './../../../../ingest_manager/common/constants/agent';
import { AgentSOAttributes } from '../../../../ingest_manager/common';
import {
getFleetSavedObjectsMetadata,
getFleetEventsSavedObjects,
} from './endpoint_telemetry_saved_objects';

export interface EndpointMetadataOSTelemetry {
export interface EndpointOSMetadataTelemetry {
name: string;
version: string;
count: number;
Expand All @@ -23,7 +25,19 @@ export interface EndpointPoliciesTelemetry {
};
}

export interface EndpointMetadataTelemetry {
total_installed: number;
active_within_last_24_hours: number;
os: Record<string, EndpointOSMetadataTelemetry>;
policies?: EndpointPoliciesTelemetry; // TODO: make required when able to enable policy information
}

export interface IngestManagerLocalMetadata extends AgentMetadata {
elastic: {
agent: {
id: string;
};
};
host: {
id: string;
};
Expand All @@ -34,73 +48,39 @@ export interface IngestManagerLocalMetadata extends AgentMetadata {
};
}

export interface EndpointMetadataTelemetry {
endpoints: {
installed: number;
last_24_hours: number;
};
os: Record<string, EndpointMetadataOSTelemetry>;
policies: EndpointPoliciesTelemetry;
}

export const getDefaultEndpointMetadataTelemetry = (): EndpointMetadataTelemetry => ({
endpoints: { installed: 0, last_24_hours: 0 },
os: {} as Record<string, EndpointMetadataOSTelemetry>,
policies: {
malware: {
success: 0,
warning: 0,
failure: 0,
},
},
export const getDefaultEndpointTelemetry = (): EndpointMetadataTelemetry => ({
total_installed: 0,
active_within_last_24_hours: 0,
os: {},
});

/**
* ---Functionality Tracking---
* Number of active endpoints (in progress)
* - Successfully installed (done)
* - Has sent response within last 24 hours (best way to go about this? May not have info?)
* See the list by customer (provided by telemetry under license.issued_to) Can most likely pull those details in the dashboards
* OS (done)
* - Type (done)
* - Version (done)
* Geolocation of endpoints (slated for 7.10): Query the agent based on the agent info in the metadata to retrieve this
* ---------------------------------
* RE Performance / usage collection timeout from Tina Helligers
* We have a timeout on waiting for collectors to report they are ready before issuing a bulkFetch method that essentially calls the fetch method for all the collectors registered.
* This timeout defaults to 60s. If you suspect that your fetch method is going to take too long (meaning data for that collector won't be reported),
* you could follow a similar approach as what we do for oss_telemetry where we use the task manager to run on a daily basis.
*/

const ENDPOINT_PACKAGE_CONSTANT = 'system'; // TODO: replace with 'endpoint' when I have endpoint package working remotely

export const getEndpointMetadataTelemetryFromFleet = async (
savedObjectsClient: ISavedObjectsRepository
savedObjectsClient: SavedObjectsClientContract
) => {
const { saved_objects: endpointAgents } = await savedObjectsClient.find<AgentSOAttributes>({
type: AGENT_SAVED_OBJECT_TYPE,
fields: ['packages', 'last_checkin', 'local_metadata'],
filter: `${AGENT_SAVED_OBJECT_TYPE}.attributes.packages: ${ENDPOINT_PACKAGE_CONSTANT}`,
});
// Retrieve every agent that references the endpoint as an installed package. It will not be listed if it was never installed
const { saved_objects: endpointAgents } = await getFleetSavedObjectsMetadata(savedObjectsClient);
const endpointTelemetry = getDefaultEndpointTelemetry();
// If there are no installed endpoints return the default telemetry object
if (!endpointAgents || endpointAgents.length < 1) return endpointTelemetry;

const uniqueHostIds: Set<string> = new Set();
const endpointTelemetry = getDefaultEndpointMetadataTelemetry();
const uniqueAgentIds: Set<string> = new Set();

// All agents who have the endpoint registered as an installed package. This shows up in the packages field of the savedObjects response.
endpointTelemetry.endpoints.installed = endpointAgents.length;
// All agents who have the endpoint registered as an installed package.
endpointTelemetry.total_installed = endpointAgents.length;

const aDayAgo = new Date();
aDayAgo.setDate(aDayAgo.getDate() - 1);

const endpointMetadataTelemetry = endpointAgents.reduce(
(metadataTelemetry, { attributes: metadataAttributes }) => {
const { last_checkin: lastCheckin, local_metadata: localMetadata } = metadataAttributes;
const { host, os } = localMetadata as IngestManagerLocalMetadata;
const { host, os, elastic } = localMetadata as IngestManagerLocalMetadata;

// If there has been a successful check in successfully in the last 24 hours, determine it as active in the last 24 hours.
if (lastCheckin && new Date(lastCheckin) > aDayAgo) {
metadataTelemetry.endpoints.last_24_hours += 1;
// Get agents that have checked in within the last 24 hours to later see if their endpoints are running
uniqueAgentIds.add(elastic.agent.id);
}

if (host && uniqueHostIds.has(host.id)) {
return metadataTelemetry;
} else {
Expand All @@ -122,7 +102,26 @@ export const getEndpointMetadataTelemetryFromFleet = async (
endpointTelemetry
);

// TODO: Update endpoint policies here if possible
// Handle Last 24 Hours
for (const agentId of uniqueAgentIds) {
const { saved_objects: agentEvents } = await getFleetEventsSavedObjects(
savedObjectsClient,
agentId
);
const lastEndpointStatus = agentEvents.find((agentEvent) =>
agentEvent.attributes.message.includes('endpoint-security')
);

/*
We can assume that if the last status of the endpoint is RUNNING and the agent has checked in within the last 24 hours
then the endpoint has still been running within the last 24 hours. If / when we get the policy response, then we can use that
instead
*/
const endpointIsActive = lastEndpointStatus?.attributes.subtype === 'RUNNING';
if (endpointIsActive) {
endpointMetadataTelemetry.active_within_last_24_hours += 1;
}
}

return endpointMetadataTelemetry;
};
Loading

0 comments on commit 9247b6a

Please sign in to comment.