Skip to content

Commit 2d3b157

Browse files
authored
[7.x] initial telemetry setup (#69330) (#71578)
1 parent 8ca6757 commit 2d3b157

File tree

12 files changed

+546
-25
lines changed

12 files changed

+546
-25
lines changed

x-pack/plugins/security_solution/server/plugin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S
114114
initSavedObjects(core.savedObjects);
115115
initUiSettings(core.uiSettings);
116116
initUsageCollectors({
117+
core,
117118
kibanaIndex: globalConfig.kibana.index,
118119
ml: plugins.ml,
119120
usageCollection: plugins.usageCollection,

x-pack/plugins/security_solution/server/usage/collector.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,32 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { LegacyAPICaller } from '../../../../../src/core/server';
7+
import { LegacyAPICaller, CoreSetup } from '../../../../../src/core/server';
88
import { CollectorDependencies } from './types';
99
import { DetectionsUsage, fetchDetectionsUsage } from './detections';
10+
import { EndpointUsage, getEndpointTelemetryFromFleet } from './endpoints';
1011

1112
export type RegisterCollector = (deps: CollectorDependencies) => void;
1213
export interface UsageData {
1314
detections: DetectionsUsage;
15+
endpoints: EndpointUsage;
1416
}
1517

16-
export const registerCollector: RegisterCollector = ({ kibanaIndex, ml, usageCollection }) => {
18+
export async function getInternalSavedObjectsClient(core: CoreSetup) {
19+
return core.getStartServices().then(async ([coreStart]) => {
20+
return coreStart.savedObjects.createInternalRepository();
21+
});
22+
}
23+
24+
export const registerCollector: RegisterCollector = ({
25+
core,
26+
kibanaIndex,
27+
ml,
28+
usageCollection,
29+
}) => {
1730
if (!usageCollection) {
1831
return;
1932
}
20-
2133
const collector = usageCollection.makeUsageCollector<UsageData>({
2234
type: 'security_solution',
2335
schema: {
@@ -43,11 +55,32 @@ export const registerCollector: RegisterCollector = ({ kibanaIndex, ml, usageCol
4355
},
4456
},
4557
},
58+
endpoints: {
59+
total_installed: { type: 'long' },
60+
active_within_last_24_hours: { type: 'long' },
61+
os: {
62+
full_name: { type: 'keyword' },
63+
platform: { type: 'keyword' },
64+
version: { type: 'keyword' },
65+
count: { type: 'long' },
66+
},
67+
policies: {
68+
malware: {
69+
success: { type: 'long' },
70+
warning: { type: 'long' },
71+
failure: { type: 'long' },
72+
},
73+
},
74+
},
4675
},
4776
isReady: () => kibanaIndex.length > 0,
48-
fetch: async (callCluster: LegacyAPICaller): Promise<UsageData> => ({
49-
detections: await fetchDetectionsUsage(kibanaIndex, callCluster, ml),
50-
}),
77+
fetch: async (callCluster: LegacyAPICaller): Promise<UsageData> => {
78+
const savedObjectsClient = await getInternalSavedObjectsClient(core);
79+
return {
80+
detections: await fetchDetectionsUsage(kibanaIndex, callCluster, ml),
81+
endpoints: await getEndpointTelemetryFromFleet(savedObjectsClient),
82+
};
83+
},
5184
});
5285

5386
usageCollection.registerCollector(collector);

x-pack/plugins/security_solution/server/usage/detections.mocks.ts renamed to x-pack/plugins/security_solution/server/usage/detections/detections.mocks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* or more contributor license agreements. Licensed under the Elastic License;
44
* you may not use this file except in compliance with the Elastic License.
55
*/
6-
import { INTERNAL_IMMUTABLE_KEY } from '../../common/constants';
6+
import { INTERNAL_IMMUTABLE_KEY } from '../../../common/constants';
77

88
export const getMockJobSummaryResponse = () => [
99
{

x-pack/plugins/security_solution/server/usage/detections.test.ts renamed to x-pack/plugins/security_solution/server/usage/detections/detections.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { LegacyAPICaller } from '../../../../../src/core/server';
8-
import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks';
9-
import { jobServiceProvider } from '../../../ml/server/models/job_service';
10-
import { DataRecognizer } from '../../../ml/server/models/data_recognizer';
11-
import { mlServicesMock } from '../lib/machine_learning/mocks';
7+
import { LegacyAPICaller } from '../../../../../../src/core/server';
8+
import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks';
9+
import { jobServiceProvider } from '../../../../ml/server/models/job_service';
10+
import { DataRecognizer } from '../../../../ml/server/models/data_recognizer';
11+
import { mlServicesMock } from '../../lib/machine_learning/mocks';
1212
import {
1313
getMockJobSummaryResponse,
1414
getMockListModulesResponse,
1515
getMockRulesResponse,
1616
} from './detections.mocks';
17-
import { fetchDetectionsUsage } from './detections';
17+
import { fetchDetectionsUsage } from './index';
1818

19-
jest.mock('../../../ml/server/models/job_service');
20-
jest.mock('../../../ml/server/models/data_recognizer');
19+
jest.mock('../../../../ml/server/models/job_service');
20+
jest.mock('../../../../ml/server/models/data_recognizer');
2121

2222
describe('Detections Usage', () => {
2323
describe('fetchDetectionsUsage()', () => {

x-pack/plugins/security_solution/server/usage/detections_helpers.ts renamed to x-pack/plugins/security_solution/server/usage/detections/detections_helpers.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
import { SearchParams } from 'elasticsearch';
88

9-
import { LegacyAPICaller, SavedObjectsClient } from '../../../../../src/core/server';
9+
import { LegacyAPICaller, SavedObjectsClient } from '../../../../../../src/core/server';
1010
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
11-
import { jobServiceProvider } from '../../../ml/server/models/job_service';
11+
import { jobServiceProvider } from '../../../../ml/server/models/job_service';
1212
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
13-
import { DataRecognizer } from '../../../ml/server/models/data_recognizer';
14-
import { MlPluginSetup } from '../../../ml/server';
15-
import { SIGNALS_ID, INTERNAL_IMMUTABLE_KEY } from '../../common/constants';
16-
import { DetectionRulesUsage, MlJobsUsage } from './detections';
17-
import { isJobStarted } from '../../common/machine_learning/helpers';
13+
import { DataRecognizer } from '../../../../ml/server/models/data_recognizer';
14+
import { MlPluginSetup } from '../../../../ml/server';
15+
import { SIGNALS_ID, INTERNAL_IMMUTABLE_KEY } from '../../../common/constants';
16+
import { DetectionRulesUsage, MlJobsUsage } from './index';
17+
import { isJobStarted } from '../../../common/machine_learning/helpers';
1818

1919
interface DetectionsMetric {
2020
isElastic: boolean;

x-pack/plugins/security_solution/server/usage/detections.ts renamed to x-pack/plugins/security_solution/server/usage/detections/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { LegacyAPICaller } from '../../../../../src/core/server';
7+
import { LegacyAPICaller } from '../../../../../../src/core/server';
88
import { getMlJobsUsage, getRulesUsage } from './detections_helpers';
9-
import { MlPluginSetup } from '../../../ml/server';
9+
import { MlPluginSetup } from '../../../../ml/server';
1010

1111
interface FeatureUsage {
1212
enabled: number;
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import { SavedObjectsFindResponse } from 'src/core/server';
7+
import { AgentEventSOAttributes } from './../../../../ingest_manager/common/types/models/agent';
8+
import {
9+
AGENT_SAVED_OBJECT_TYPE,
10+
AGENT_EVENT_SAVED_OBJECT_TYPE,
11+
} from '../../../../ingest_manager/common/constants/agent';
12+
import { Agent } from '../../../../ingest_manager/common';
13+
import { FLEET_ENDPOINT_PACKAGE_CONSTANT } from './fleet_saved_objects';
14+
15+
const testAgentId = 'testAgentId';
16+
const testConfigId = 'testConfigId';
17+
18+
/** Mock OS Platform for endpoint telemetry */
19+
export const MockOSPlatform = 'somePlatform';
20+
/** Mock OS Name for endpoint telemetry */
21+
export const MockOSName = 'somePlatformName';
22+
/** Mock OS Version for endpoint telemetry */
23+
export const MockOSVersion = '1';
24+
/** Mock OS Full Name for endpoint telemetry */
25+
export const MockOSFullName = 'somePlatformFullName';
26+
27+
/**
28+
*
29+
* @param lastCheckIn - the last time the agent checked in. Defaults to current ISO time.
30+
* @description We request the install and OS related telemetry information from the 'fleet-agents' saved objects in ingest_manager. This mocks that response
31+
*/
32+
export const mockFleetObjectsResponse = (
33+
lastCheckIn = new Date().toISOString()
34+
): SavedObjectsFindResponse<Agent> => ({
35+
page: 1,
36+
per_page: 20,
37+
total: 1,
38+
saved_objects: [
39+
{
40+
type: AGENT_SAVED_OBJECT_TYPE,
41+
id: testAgentId,
42+
attributes: {
43+
active: true,
44+
id: testAgentId,
45+
config_id: 'randoConfigId',
46+
type: 'PERMANENT',
47+
user_provided_metadata: {},
48+
enrolled_at: lastCheckIn,
49+
current_error_events: [],
50+
local_metadata: {
51+
elastic: {
52+
agent: {
53+
id: testAgentId,
54+
},
55+
},
56+
host: {
57+
hostname: 'testDesktop',
58+
name: 'testDesktop',
59+
id: 'randoHostId',
60+
},
61+
os: {
62+
platform: MockOSPlatform,
63+
version: MockOSVersion,
64+
name: MockOSName,
65+
full: MockOSFullName,
66+
},
67+
},
68+
packages: [FLEET_ENDPOINT_PACKAGE_CONSTANT, 'system'],
69+
last_checkin: lastCheckIn,
70+
},
71+
references: [],
72+
updated_at: lastCheckIn,
73+
version: 'WzI4MSwxXQ==',
74+
score: 0,
75+
},
76+
],
77+
});
78+
79+
/**
80+
*
81+
* @param running - allows us to set whether the mocked endpoint is in an active or disabled/failed state
82+
* @param updatedDate - the last time the endpoint was updated. Defaults to current ISO time.
83+
* @description We request the events triggered by the agent and get the most recent endpoint event to confirm it is still running. This allows us to mock both scenarios
84+
*/
85+
export const mockFleetEventsObjectsResponse = (
86+
running?: boolean,
87+
updatedDate = new Date().toISOString()
88+
): SavedObjectsFindResponse<AgentEventSOAttributes> => {
89+
return {
90+
page: 1,
91+
per_page: 20,
92+
total: 2,
93+
saved_objects: [
94+
{
95+
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
96+
id: 'id1',
97+
attributes: {
98+
agent_id: testAgentId,
99+
type: running ? 'STATE' : 'ERROR',
100+
timestamp: updatedDate,
101+
subtype: running ? 'RUNNING' : 'FAILED',
102+
message: `Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to ${
103+
running ? 'RUNNING' : 'FAILED'
104+
}: `,
105+
config_id: testConfigId,
106+
},
107+
references: [],
108+
updated_at: updatedDate,
109+
version: 'WzExOCwxXQ==',
110+
score: 0,
111+
},
112+
{
113+
type: AGENT_EVENT_SAVED_OBJECT_TYPE,
114+
id: 'id2',
115+
attributes: {
116+
agent_id: testAgentId,
117+
type: 'STATE',
118+
timestamp: updatedDate,
119+
subtype: 'STARTING',
120+
message:
121+
'Application: endpoint-security--8.0.0[d8f7f6e8-9375-483c-b456-b479f1d7a4f2]: State changed to STARTING: Starting',
122+
config_id: testConfigId,
123+
},
124+
references: [],
125+
updated_at: updatedDate,
126+
version: 'WzExNywxXQ==',
127+
score: 0,
128+
},
129+
],
130+
};
131+
};

0 commit comments

Comments
 (0)