mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Osquery] Remove osquery-manager-usage-metric SO type (#154100)
## Summary Remove unused `osquery-manager-usage-metric` SO type --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
3db3709eb4
commit
7a62628bd6
17 changed files with 5 additions and 967 deletions
|
@ -97,6 +97,11 @@ export const initialExcludeOnUpgradeQueryMock = {
|
|||
type: 'ml-telemetry',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
type: 'osquery-manager-usage-metric',
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
type: 'osquery-usage-metric',
|
||||
|
|
|
@ -9,10 +9,6 @@ export const savedQuerySavedObjectType = 'osquery-saved-query';
|
|||
export const packSavedObjectType = 'osquery-pack';
|
||||
export const packAssetSavedObjectType = 'osquery-pack-asset';
|
||||
export const usageMetricSavedObjectType = 'osquery-manager-usage-metric';
|
||||
export type SavedObjectType =
|
||||
| 'osquery-saved-query'
|
||||
| 'osquery-pack'
|
||||
| 'osquery-manager-usage-metric';
|
||||
|
||||
/**
|
||||
* This makes any optional property the same as Required<T> would but also has the
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
"optionalPlugins": [
|
||||
"fleet",
|
||||
"home",
|
||||
"usageCollection",
|
||||
"lens",
|
||||
"telemetry",
|
||||
"cases"
|
||||
|
|
|
@ -27,7 +27,6 @@ import type { OsqueryPluginSetup, OsqueryPluginStart, SetupPlugins, StartPlugins
|
|||
import { defineRoutes } from './routes';
|
||||
import { osquerySearchStrategyProvider } from './search_strategy/osquery';
|
||||
import { initSavedObjects } from './saved_objects';
|
||||
import { initUsageCollectors } from './usage';
|
||||
import type { OsqueryAppContext } from './lib/osquery_app_context_services';
|
||||
import { OsqueryAppContextService } from './lib/osquery_app_context_services';
|
||||
import type { ConfigType } from '../common/config';
|
||||
|
@ -78,11 +77,6 @@ export class OsqueryPlugin implements Plugin<OsqueryPluginSetup, OsqueryPluginSt
|
|||
};
|
||||
|
||||
initSavedObjects(core.savedObjects);
|
||||
initUsageCollectors({
|
||||
core,
|
||||
osqueryContext,
|
||||
usageCollection: plugins.usageCollection,
|
||||
});
|
||||
|
||||
this.createActionService = createActionService(osqueryContext);
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './recorder';
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { savedObjectsClientMock } from '@kbn/core/server/mocks';
|
||||
|
||||
import { usageMetricSavedObjectType } from '../../../common/types';
|
||||
|
||||
import type { CounterValue, RouteString } from './recorder';
|
||||
import { getOrCreateMetricObject, getRouteMetric, incrementCount, routeStrings } from './recorder';
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function checkGetCalls(calls: any[]) {
|
||||
expect(calls.length).toEqual(routeStrings.length);
|
||||
for (let i = 0; i < routeStrings.length; ++i) {
|
||||
expect(calls[i]).toEqual([usageMetricSavedObjectType, routeStrings[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function checkCreateCalls(calls: any[], expectedCallRoutes: string[] = routeStrings) {
|
||||
expect(calls.length).toEqual(expectedCallRoutes.length);
|
||||
for (let i = 0; i < expectedCallRoutes.length; ++i) {
|
||||
expect(calls[i][0]).toEqual(usageMetricSavedObjectType);
|
||||
expect(calls[i][2].id).toEqual(expectedCallRoutes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Usage metric recorder', () => {
|
||||
describe('Metric initalizer', () => {
|
||||
const get = savedObjectsClient.get as jest.Mock;
|
||||
const create = savedObjectsClient.create as jest.Mock;
|
||||
afterEach(() => {
|
||||
get.mockClear();
|
||||
create.mockClear();
|
||||
});
|
||||
it('should create metrics that do not exist', async () => {
|
||||
get.mockRejectedValueOnce('stub value');
|
||||
create.mockReturnValueOnce('stub value');
|
||||
const result = await getOrCreateMetricObject(savedObjectsClient, 'live_query');
|
||||
checkGetCalls(get.mock.calls);
|
||||
checkCreateCalls(create.mock.calls);
|
||||
expect(result).toBe('stub value');
|
||||
});
|
||||
|
||||
it('should handle previously created objects properly', async () => {
|
||||
get.mockReturnValueOnce('stub value');
|
||||
create.mockRejectedValueOnce('stub value');
|
||||
const result = await getOrCreateMetricObject(savedObjectsClient, 'live_query');
|
||||
checkGetCalls(get.mock.calls);
|
||||
checkCreateCalls(create.mock.calls, []);
|
||||
expect(result).toBe('stub value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Incrementation', () => {
|
||||
let counterMap: { [key: string]: CounterValue };
|
||||
const get = savedObjectsClient.get as jest.Mock;
|
||||
const update = savedObjectsClient.update as jest.Mock;
|
||||
update.mockImplementation(
|
||||
async (objectType: string, route: RouteString, newVal: CounterValue) => {
|
||||
counterMap[`${objectType}-${route}`] = newVal;
|
||||
}
|
||||
);
|
||||
get.mockImplementation(async (objectType: string, route: RouteString) => ({
|
||||
attributes: counterMap[`${objectType}-${route}`],
|
||||
}));
|
||||
beforeEach(() => {
|
||||
counterMap = routeStrings.reduce((acc, route) => {
|
||||
acc[`${usageMetricSavedObjectType}-${route}`] = {
|
||||
count: 0,
|
||||
errors: 0,
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {} as { [key: string]: CounterValue });
|
||||
get.mockClear();
|
||||
update.mockClear();
|
||||
});
|
||||
it('should increment the route counter', async () => {
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 0,
|
||||
errors: 0,
|
||||
});
|
||||
await incrementCount(savedObjectsClient, 'live_query');
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 1,
|
||||
errors: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow incrementing the error counter', async () => {
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 0,
|
||||
errors: 0,
|
||||
});
|
||||
await incrementCount(savedObjectsClient, 'live_query', 'errors');
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 0,
|
||||
errors: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow adjustment of the increment', async () => {
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 0,
|
||||
errors: 0,
|
||||
});
|
||||
await incrementCount(savedObjectsClient, 'live_query', 'count', 2);
|
||||
expect(await getRouteMetric(savedObjectsClient, 'live_query')).toEqual({
|
||||
count: 2,
|
||||
errors: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import { usageMetricSavedObjectType } from '../../../common/types';
|
||||
import type { LiveQuerySessionUsage } from '../../usage/types';
|
||||
|
||||
export interface RouteUsageMetric {
|
||||
queries: number;
|
||||
errors: number;
|
||||
}
|
||||
|
||||
export type RouteString = 'live_query';
|
||||
|
||||
export const routeStrings: RouteString[] = ['live_query'];
|
||||
|
||||
export async function getOrCreateMetricObject<T>(
|
||||
soClient: SavedObjectsClientContract,
|
||||
route: string
|
||||
) {
|
||||
try {
|
||||
return await soClient.get<T>(usageMetricSavedObjectType, route);
|
||||
} catch (e) {
|
||||
return await soClient.create(
|
||||
usageMetricSavedObjectType,
|
||||
{
|
||||
errors: 0,
|
||||
count: 0,
|
||||
},
|
||||
{
|
||||
id: route,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCount(soClient: SavedObjectsClientContract, route: RouteString) {
|
||||
return await getOrCreateMetricObject<LiveQuerySessionUsage>(soClient, route);
|
||||
}
|
||||
|
||||
export interface CounterValue {
|
||||
count: number;
|
||||
errors: number;
|
||||
}
|
||||
|
||||
export async function incrementCount(
|
||||
soClient: SavedObjectsClientContract,
|
||||
route: RouteString,
|
||||
key: keyof CounterValue = 'count',
|
||||
increment = 1
|
||||
) {
|
||||
const metric = await getOrCreateMetricObject<CounterValue>(soClient, route);
|
||||
metric.attributes[key] += increment;
|
||||
await soClient.update(usageMetricSavedObjectType, route, metric.attributes);
|
||||
}
|
||||
|
||||
export async function getRouteMetric(soClient: SavedObjectsClientContract, route: RouteString) {
|
||||
return (await getCount(soClient, route)).attributes;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SavedObjectsType } from '@kbn/core/server';
|
||||
|
||||
import { usageMetricSavedObjectType } from '../../../common/types';
|
||||
|
||||
export const usageMetricSavedObjectMappings: SavedObjectsType['mappings'] = {
|
||||
properties: {
|
||||
count: {
|
||||
type: 'long',
|
||||
},
|
||||
errors: {
|
||||
type: 'long',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const usageMetricType: SavedObjectsType = {
|
||||
name: usageMetricSavedObjectType,
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
mappings: usageMetricSavedObjectMappings,
|
||||
};
|
|
@ -13,7 +13,6 @@ import type {
|
|||
} from '@kbn/data-plugin/server';
|
||||
|
||||
import type { FleetStartContract } from '@kbn/fleet-plugin/server';
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import type { PluginSetupContract } from '@kbn/features-plugin/server';
|
||||
import type { SecurityPluginStart } from '@kbn/security-plugin/server';
|
||||
import type {
|
||||
|
@ -34,7 +33,6 @@ export interface OsqueryPluginSetup {
|
|||
export interface OsqueryPluginStart {}
|
||||
|
||||
export interface SetupPlugins {
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
actions: ActionsPlugin['setup'];
|
||||
cases: CasesSetup;
|
||||
data: DataPluginSetup;
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CoreSetup } from '@kbn/core/server';
|
||||
import { SavedObjectsClient } from '@kbn/core/server';
|
||||
import type { CollectorFetchContext } from '@kbn/usage-collection-plugin/server';
|
||||
import { getBeatUsage, getLiveQueryUsage, getPolicyLevelUsage } from './fetchers';
|
||||
import type { CollectorDependencies, UsageData } from './types';
|
||||
import { usageSchema } from './types';
|
||||
|
||||
export type RegisterCollector = (deps: CollectorDependencies) => void;
|
||||
export const getInternalSavedObjectsClient = async (
|
||||
getStartServices: CoreSetup['getStartServices']
|
||||
) => {
|
||||
const [coreStart] = await getStartServices();
|
||||
|
||||
return new SavedObjectsClient(coreStart.savedObjects.createInternalRepository());
|
||||
};
|
||||
|
||||
export const registerCollector: RegisterCollector = ({ core, osqueryContext, usageCollection }) => {
|
||||
if (!usageCollection) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collector = usageCollection.makeUsageCollector<UsageData>({
|
||||
type: 'osquery',
|
||||
schema: usageSchema,
|
||||
isReady: () => true,
|
||||
fetch: async ({ esClient }: CollectorFetchContext): Promise<UsageData> => {
|
||||
const savedObjectsClient = await getInternalSavedObjectsClient(core.getStartServices);
|
||||
|
||||
return {
|
||||
beat_metrics: {
|
||||
usage: await getBeatUsage(esClient),
|
||||
},
|
||||
live_query_usage: await getLiveQueryUsage(savedObjectsClient, esClient),
|
||||
...(await getPolicyLevelUsage(
|
||||
esClient,
|
||||
savedObjectsClient,
|
||||
osqueryContext.service.getPackagePolicyService()
|
||||
)),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
usageCollection.registerCollector(collector);
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const METRICS_INDICES = 'logs-elastic_agent.osquerybeat*';
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { extractBeatUsageMetrics } from './fetchers';
|
||||
|
||||
describe('extractBeatUsageMetrics', () => {
|
||||
it('should not blow when no values are supplied for the aggregations', () => {
|
||||
expect(extractBeatUsageMetrics({})).toEqual({
|
||||
memory: {
|
||||
rss: {},
|
||||
},
|
||||
cpu: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not blow when some values are missing from the aggregations', () => {
|
||||
expect(
|
||||
extractBeatUsageMetrics({
|
||||
aggregations: {
|
||||
lastDay: {
|
||||
max_rss: {
|
||||
value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
memory: {
|
||||
rss: {
|
||||
max: 1,
|
||||
},
|
||||
},
|
||||
cpu: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should pick out all the max/avg/latest for memory/cpu', () => {
|
||||
expect(
|
||||
extractBeatUsageMetrics({
|
||||
aggregations: {
|
||||
lastDay: {
|
||||
max_rss: {
|
||||
value: 1,
|
||||
},
|
||||
avg_rss: {
|
||||
value: 1,
|
||||
},
|
||||
max_cpu: {
|
||||
value: 2,
|
||||
},
|
||||
avg_cpu: {
|
||||
value: 2,
|
||||
},
|
||||
latest: {
|
||||
hits: {
|
||||
total: 1,
|
||||
hits: [
|
||||
{
|
||||
_index: '',
|
||||
_id: '',
|
||||
_source: {
|
||||
monitoring: {
|
||||
metrics: {
|
||||
beat: {
|
||||
cpu: {
|
||||
total: {
|
||||
time: {
|
||||
ms: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
memstats: {
|
||||
rss: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
memory: {
|
||||
rss: {
|
||||
max: 1,
|
||||
avg: 1,
|
||||
latest: 1,
|
||||
},
|
||||
},
|
||||
cpu: {
|
||||
max: 2,
|
||||
avg: 2,
|
||||
latest: 2,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,274 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type {
|
||||
AggregationsSingleBucketAggregateBase,
|
||||
AggregationsTopHitsAggregate,
|
||||
AggregationsRateAggregate,
|
||||
SearchResponse,
|
||||
} from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import type { PackagePolicyClient } from '@kbn/fleet-plugin/server';
|
||||
import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server';
|
||||
import type { ListResult, PackagePolicy } from '@kbn/fleet-plugin/common';
|
||||
import {
|
||||
AGENTS_INDEX,
|
||||
AGENT_ACTIONS_INDEX,
|
||||
PACKAGE_POLICY_SAVED_OBJECT_TYPE,
|
||||
} from '@kbn/fleet-plugin/common';
|
||||
import { getRouteMetric } from '../routes/usage';
|
||||
import { OSQUERY_INTEGRATION_NAME } from '../../common';
|
||||
import { METRICS_INDICES } from './constants';
|
||||
import type { AgentInfo, BeatMetricsUsage, LiveQueryUsage } from './types';
|
||||
|
||||
interface PolicyLevelUsage {
|
||||
scheduled_queries?: ScheduledQueryUsageMetrics;
|
||||
agent_info?: AgentInfo;
|
||||
}
|
||||
|
||||
export async function getPolicyLevelUsage(
|
||||
esClient: ElasticsearchClient,
|
||||
soClient: SavedObjectsClientContract,
|
||||
packagePolicyService?: PackagePolicyClient
|
||||
): Promise<PolicyLevelUsage> {
|
||||
if (!packagePolicyService) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const packagePolicies = await packagePolicyService.list(soClient, {
|
||||
kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${OSQUERY_INTEGRATION_NAME}`,
|
||||
perPage: 10_000,
|
||||
});
|
||||
|
||||
const result: PolicyLevelUsage = {
|
||||
scheduled_queries: getScheduledQueryUsage(packagePolicies),
|
||||
// TODO: figure out how to support dynamic keys in metrics
|
||||
// packageVersions: getPackageVersions(packagePolicies),
|
||||
};
|
||||
const agentResponse = await esClient.search<
|
||||
unknown,
|
||||
{
|
||||
policied: AggregationsSingleBucketAggregateBase;
|
||||
}
|
||||
>({
|
||||
body: {
|
||||
size: 0,
|
||||
query: {
|
||||
match: {
|
||||
active: true,
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
policied: {
|
||||
filter: {
|
||||
terms: {
|
||||
policy_id: packagePolicies.items.map((p) => p.policy_id),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
index: AGENTS_INDEX,
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
const policied = agentResponse.aggregations?.policied;
|
||||
if (policied && typeof policied.doc_count === 'number') {
|
||||
result.agent_info = {
|
||||
enrolled: policied.doc_count,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getPackageVersions(packagePolicies: ListResult<PackagePolicy>) {
|
||||
return packagePolicies.items.reduce((acc, item) => {
|
||||
if (item.package) {
|
||||
acc[item.package.version] = (acc[item.package.version] ?? 0) + 1;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as { [version: string]: number });
|
||||
}
|
||||
|
||||
interface ScheduledQueryUsageMetrics {
|
||||
queryGroups: {
|
||||
total: number;
|
||||
empty: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function getScheduledQueryUsage(packagePolicies: ListResult<PackagePolicy>) {
|
||||
return packagePolicies.items.reduce(
|
||||
(acc, item) => {
|
||||
++acc.queryGroups.total;
|
||||
const policyAgents = item.inputs.reduce((sum, input) => sum + input.streams.length, 0);
|
||||
if (policyAgents === 0) {
|
||||
++acc.queryGroups.empty;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
queryGroups: {
|
||||
total: 0,
|
||||
empty: 0,
|
||||
},
|
||||
} as ScheduledQueryUsageMetrics
|
||||
);
|
||||
}
|
||||
|
||||
export async function getLiveQueryUsage(
|
||||
soClient: SavedObjectsClientContract,
|
||||
esClient: ElasticsearchClient
|
||||
) {
|
||||
const metricResponse = await esClient.search<
|
||||
unknown,
|
||||
{
|
||||
queries: AggregationsSingleBucketAggregateBase;
|
||||
}
|
||||
>({
|
||||
body: {
|
||||
size: 0,
|
||||
aggs: {
|
||||
queries: {
|
||||
filter: {
|
||||
term: {
|
||||
input_type: 'osquery',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
index: AGENT_ACTIONS_INDEX,
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
const result: LiveQueryUsage = {
|
||||
session: await getRouteMetric(soClient, 'live_query'),
|
||||
};
|
||||
const esQueries = metricResponse.aggregations?.queries;
|
||||
if (esQueries && typeof esQueries.doc_count === 'number') {
|
||||
// getting error stats out of ES is difficult due to a lack of error info on .fleet-actions
|
||||
// and a lack of indexable osquery specific info on .fleet-actions-results
|
||||
result.cumulative = {
|
||||
queries: esQueries.doc_count,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
interface BeatUsageAggs {
|
||||
lastDay: {
|
||||
max_rss?: AggregationsRateAggregate;
|
||||
max_cpu?: AggregationsRateAggregate;
|
||||
latest?: AggregationsTopHitsAggregate;
|
||||
|
||||
// not used in code, declared to satisfy type
|
||||
avg_rss?: AggregationsRateAggregate;
|
||||
avg_cpu?: AggregationsRateAggregate;
|
||||
};
|
||||
}
|
||||
|
||||
export function extractBeatUsageMetrics(
|
||||
metricResponse: Pick<SearchResponse<unknown, BeatUsageAggs>, 'aggregations'>
|
||||
) {
|
||||
const lastDayAggs = metricResponse.aggregations?.lastDay;
|
||||
const result: BeatMetricsUsage = {
|
||||
memory: {
|
||||
rss: {},
|
||||
},
|
||||
cpu: {},
|
||||
};
|
||||
|
||||
if (lastDayAggs) {
|
||||
if (lastDayAggs.max_rss !== undefined) {
|
||||
result.memory.rss.max = lastDayAggs.max_rss.value;
|
||||
}
|
||||
|
||||
if (lastDayAggs.avg_rss !== undefined) {
|
||||
// @ts-expect-error condition check another property, not idea why. consider fixing
|
||||
result.memory.rss.avg = lastDayAggs.max_rss.value;
|
||||
}
|
||||
|
||||
if (lastDayAggs.max_cpu !== undefined) {
|
||||
result.cpu.max = lastDayAggs.max_cpu.value;
|
||||
}
|
||||
|
||||
if (lastDayAggs.avg_cpu !== undefined) {
|
||||
// @ts-expect-error condition check another property, not idea why. consider fixing
|
||||
result.cpu.avg = lastDayAggs.max_cpu.value;
|
||||
}
|
||||
|
||||
if (lastDayAggs.latest !== undefined) {
|
||||
const latest = lastDayAggs.latest.hits.hits[0]?._source?.monitoring.metrics.beat;
|
||||
if (latest) {
|
||||
result.cpu.latest = latest.cpu.total.time.ms;
|
||||
result.memory.rss.latest = latest.memstats.rss;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getBeatUsage(esClient: ElasticsearchClient) {
|
||||
const metricResponse = await esClient.search<unknown, BeatUsageAggs>({
|
||||
body: {
|
||||
size: 0,
|
||||
aggs: {
|
||||
lastDay: {
|
||||
filter: {
|
||||
range: {
|
||||
'@timestamp': {
|
||||
gte: 'now-24h',
|
||||
lte: 'now',
|
||||
},
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
latest: {
|
||||
top_hits: {
|
||||
sort: [
|
||||
{
|
||||
'@timestamp': {
|
||||
order: 'desc',
|
||||
},
|
||||
},
|
||||
],
|
||||
size: 1,
|
||||
},
|
||||
},
|
||||
max_rss: {
|
||||
max: {
|
||||
field: 'monitoring.metrics.beat.memstats.rss',
|
||||
},
|
||||
},
|
||||
avg_rss: {
|
||||
avg: {
|
||||
field: 'monitoring.metrics.beat.memstats.rss',
|
||||
},
|
||||
},
|
||||
max_cpu: {
|
||||
max: {
|
||||
field: 'monitoring.metrics.beat.cpu.total.time.ms',
|
||||
},
|
||||
},
|
||||
avg_cpu: {
|
||||
avg: {
|
||||
field: 'monitoring.metrics.beat.cpu.total.time.ms',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
index: METRICS_INDICES,
|
||||
ignore_unavailable: true,
|
||||
});
|
||||
|
||||
return extractBeatUsageMetrics(metricResponse);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CollectorDependencies } from './types';
|
||||
import { registerCollector } from './collector';
|
||||
|
||||
export type InitUsageCollectors = (deps: CollectorDependencies) => void;
|
||||
|
||||
export const initUsageCollectors: InitUsageCollectors = (dependencies) => {
|
||||
registerCollector(dependencies);
|
||||
};
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CoreSetup } from '@kbn/core/server';
|
||||
import type { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server';
|
||||
import type { OsqueryAppContext } from '../lib/osquery_app_context_services';
|
||||
import type { SetupPlugins } from '../types';
|
||||
|
||||
export type CollectorDependencies = {
|
||||
osqueryContext: OsqueryAppContext;
|
||||
core: CoreSetup;
|
||||
} & Pick<SetupPlugins, 'usageCollection'>;
|
||||
|
||||
export interface LiveQuerySessionUsage {
|
||||
count: number;
|
||||
errors: number;
|
||||
}
|
||||
export interface LiveQueryCumulativeUsage {
|
||||
queries: number;
|
||||
}
|
||||
|
||||
export interface LiveQueryUsage {
|
||||
session: LiveQuerySessionUsage;
|
||||
cumulative?: LiveQueryCumulativeUsage;
|
||||
}
|
||||
|
||||
export interface ScheduledQueryUsage {
|
||||
queryGroups: {
|
||||
total: number;
|
||||
empty: number;
|
||||
};
|
||||
}
|
||||
export interface AgentInfo {
|
||||
enrolled: number;
|
||||
}
|
||||
|
||||
export interface MetricEntry {
|
||||
max?: number;
|
||||
latest?: number;
|
||||
avg?: number;
|
||||
}
|
||||
|
||||
export interface BeatMetricsUsage {
|
||||
cpu: MetricEntry;
|
||||
memory: {
|
||||
rss: MetricEntry;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BeatMetrics {
|
||||
usage: BeatMetricsUsage;
|
||||
}
|
||||
|
||||
export interface UsageData {
|
||||
live_query_usage?: LiveQueryUsage;
|
||||
scheduled_queries?: ScheduledQueryUsage;
|
||||
agent_info?: AgentInfo;
|
||||
beat_metrics?: BeatMetrics;
|
||||
}
|
||||
|
||||
export const usageSchema: MakeSchemaFrom<UsageData> = {
|
||||
live_query_usage: {
|
||||
session: {
|
||||
count: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of osquery action requests',
|
||||
},
|
||||
},
|
||||
errors: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of osquery action requests that resulted in errors',
|
||||
},
|
||||
},
|
||||
},
|
||||
cumulative: {
|
||||
queries: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of osquery actions stored in Elasticsearch',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scheduled_queries: {
|
||||
queryGroups: {
|
||||
total: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of osquery policies/query groups',
|
||||
},
|
||||
},
|
||||
empty: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of empty osquery policies/query groups',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
agent_info: {
|
||||
enrolled: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Number of agents enrolled in a policy with an osquery integration',
|
||||
},
|
||||
},
|
||||
},
|
||||
beat_metrics: {
|
||||
usage: {
|
||||
cpu: {
|
||||
latest: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Latest cpu usage sample in ms',
|
||||
},
|
||||
},
|
||||
max: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Max cpu usage sample over 24 hours in ms',
|
||||
},
|
||||
},
|
||||
avg: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Mean cpu usage over 24 hours in ms',
|
||||
},
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
rss: {
|
||||
latest: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Latest resident set size sample',
|
||||
},
|
||||
},
|
||||
max: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Max resident set size sample over 24 hours',
|
||||
},
|
||||
},
|
||||
avg: {
|
||||
type: 'long',
|
||||
_meta: {
|
||||
description: 'Mean resident set size sample over 24 hours',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -57,7 +57,6 @@
|
|||
"@kbn/dev-cli-runner",
|
||||
"@kbn/telemetry-plugin",
|
||||
"@kbn/actions-plugin",
|
||||
"@kbn/usage-collection-plugin",
|
||||
"@kbn/features-plugin",
|
||||
"@kbn/task-manager-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
|
|
|
@ -6940,126 +6940,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"osquery": {
|
||||
"properties": {
|
||||
"live_query_usage": {
|
||||
"properties": {
|
||||
"session": {
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of osquery action requests"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of osquery action requests that resulted in errors"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cumulative": {
|
||||
"properties": {
|
||||
"queries": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of osquery actions stored in Elasticsearch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scheduled_queries": {
|
||||
"properties": {
|
||||
"queryGroups": {
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of osquery policies/query groups"
|
||||
}
|
||||
},
|
||||
"empty": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of empty osquery policies/query groups"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"agent_info": {
|
||||
"properties": {
|
||||
"enrolled": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Number of agents enrolled in a policy with an osquery integration"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"beat_metrics": {
|
||||
"properties": {
|
||||
"usage": {
|
||||
"properties": {
|
||||
"cpu": {
|
||||
"properties": {
|
||||
"latest": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Latest cpu usage sample in ms"
|
||||
}
|
||||
},
|
||||
"max": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Max cpu usage sample over 24 hours in ms"
|
||||
}
|
||||
},
|
||||
"avg": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Mean cpu usage over 24 hours in ms"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"memory": {
|
||||
"properties": {
|
||||
"rss": {
|
||||
"properties": {
|
||||
"latest": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Latest resident set size sample"
|
||||
}
|
||||
},
|
||||
"max": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Max resident set size sample over 24 hours"
|
||||
}
|
||||
},
|
||||
"avg": {
|
||||
"type": "long",
|
||||
"_meta": {
|
||||
"description": "Mean resident set size sample over 24 hours"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"reporting": {
|
||||
"properties": {
|
||||
"csv_searchsource": {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue