Skip to content
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

[Fleet] Task to publish Agent metrics #168435

Merged
merged 27 commits into from Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aa8f2a6
fleet metrics task
juliaElastic Oct 10, 2023
0627b36
added upgrading_step aggregation
juliaElastic Oct 10, 2023
bf746e9
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Oct 10, 2023
c302b2f
fixed test
juliaElastic Oct 10, 2023
82f89b9
Merge branch 'main' into fleet-metrics
juliaElastic Oct 10, 2023
6ac0546
calculating unhealthy_reason
juliaElastic Oct 10, 2023
a6345e9
enabled write to es, populating generic fields
juliaElastic Oct 11, 2023
0389572
fix import
juliaElastic Oct 11, 2023
3fe4073
fixed mock
juliaElastic Oct 11, 2023
7e3ce02
Merge branch 'main' into fleet-metrics
juliaElastic Oct 11, 2023
df49e79
added unit tests
juliaElastic Oct 11, 2023
29e8671
removed unhealthy_reason for now
juliaElastic Oct 11, 2023
fbd503b
Merge branch 'main' into fleet-metrics
juliaElastic Oct 12, 2023
aaa9b66
changed to agent fields
juliaElastic Oct 12, 2023
5ed395c
retry, using bulk
juliaElastic Oct 12, 2023
725a688
added test for bulk error
juliaElastic Oct 12, 2023
d63f3df
added fleet_server package to serverless, allow to be installed
juliaElastic Oct 12, 2023
b1c3240
updated task version to 1.0.0
juliaElastic Oct 12, 2023
c4ba9d2
Merge branch 'main' into fleet-metrics
juliaElastic Oct 16, 2023
93a6e7e
Merge branch 'main' into fleet-metrics
juliaElastic Oct 17, 2023
eeae806
fixed test
juliaElastic Oct 17, 2023
e866a71
Merge branch 'main' into fleet-metrics
juliaElastic Oct 17, 2023
b038d5d
adding fleet_server package in oblt and security projects only
juliaElastic Oct 17, 2023
f1de97b
disable fleet task in functional tests
juliaElastic Oct 17, 2023
8d439cc
disable fleet task
juliaElastic Oct 17, 2023
2af5949
Merge branch 'main' into fleet-metrics
juliaElastic Oct 18, 2023
b0add0c
revert serverless disable task, skip failing test
juliaElastic Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/serverless.yml
Expand Up @@ -8,6 +8,10 @@ xpack.fleet.internal.disableProxies: true
xpack.fleet.internal.activeAgentsSoftLimit: 25000
xpack.fleet.internal.onlyAllowAgentUpgradeToKnownVersions: true
xpack.fleet.internal.retrySetupOnBoot: true
# fleet_server package installed to publish agent metrics
xpack.fleet.packages:
- name: fleet_server
version: latest

# Cloud links
xpack.cloud.base_url: 'https://cloud.elastic.co'
Expand Down
12 changes: 6 additions & 6 deletions x-pack/plugins/fleet/server/collectors/agent_collectors.ts
Expand Up @@ -58,13 +58,13 @@ export const getAgentUsage = async (
};
};

export interface AgentPerVersion {
version: string;
count: number;
}

export interface AgentData {
agents_per_version: Array<
{
version: string;
count: number;
} & AgentStatus
>;
agents_per_version: Array<AgentPerVersion & AgentStatus>;
agent_checkin_status: {
error: number;
degraded: number;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/fleet/server/mocks/index.ts
Expand Up @@ -84,6 +84,7 @@ export const createAppContextStartContractMock = (
config$,
kibanaVersion: '8.99.0', // Fake version :)
kibanaBranch: 'main',
kibanaInstanceId: '1',
telemetryEventsSender: createMockTelemetryEventsSender(),
bulkActionsResolver: {} as any,
messageSigningService: createMessageSigningServiceMock(),
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/fleet/server/plugin.ts
Expand Up @@ -130,6 +130,8 @@ import { FleetActionsClient, type FleetActionsClientInterface } from './services
import type { FilesClientFactory } from './services/files/types';
import { PolicyWatcher } from './services/agent_policy_watch';
import { getPackageSpecTagId } from './services/epm/kibana/assets/tag_assets';
import { FleetMetricsTask } from './services/metrics/fleet_metrics_task';
import { fetchAgentMetrics } from './services/metrics/fetch_agent_metrics';

export interface FleetSetupDeps {
security: SecurityPluginSetup;
Expand Down Expand Up @@ -167,6 +169,7 @@ export interface FleetAppContext {
isProductionMode: PluginInitializerContext['env']['mode']['prod'];
kibanaVersion: PluginInitializerContext['env']['packageInfo']['version'];
kibanaBranch: PluginInitializerContext['env']['packageInfo']['branch'];
kibanaInstanceId: PluginInitializerContext['env']['instanceUuid'];
cloud?: CloudSetup;
logger?: Logger;
httpSetup?: HttpServiceSetup;
Expand Down Expand Up @@ -251,6 +254,7 @@ export class FleetPlugin
private isProductionMode: FleetAppContext['isProductionMode'];
private kibanaVersion: FleetAppContext['kibanaVersion'];
private kibanaBranch: FleetAppContext['kibanaBranch'];
private kibanaInstanceId: FleetAppContext['kibanaInstanceId'];
private httpSetup?: HttpServiceSetup;
private securitySetup!: SecurityPluginSetup;
private encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup;
Expand All @@ -259,6 +263,7 @@ export class FleetPlugin
private bulkActionsResolver?: BulkActionsResolver;
private fleetUsageSender?: FleetUsageSender;
private checkDeletedFilesTask?: CheckDeletedFilesTask;
private fleetMetricsTask?: FleetMetricsTask;

private agentService?: AgentService;
private packageService?: PackageService;
Expand All @@ -270,6 +275,7 @@ export class FleetPlugin
this.isProductionMode = this.initializerContext.env.mode.prod;
this.kibanaVersion = this.initializerContext.env.packageInfo.version;
this.kibanaBranch = this.initializerContext.env.packageInfo.branch;
this.kibanaInstanceId = this.initializerContext.env.instanceUuid;
this.logger = this.initializerContext.logger.get();
this.configInitialValue = this.initializerContext.config.get();
this.telemetryEventsSender = new TelemetryEventsSender(this.logger.get('telemetry_events'));
Expand Down Expand Up @@ -440,6 +446,10 @@ export class FleetPlugin
this.fleetUsageSender = new FleetUsageSender(deps.taskManager, core, fetch);
registerFleetUsageLogger(deps.taskManager, async () => fetchAgentsUsage(core, config));

const fetchAgents = async (abortController: AbortController) =>
await fetchAgentMetrics(core, abortController);
this.fleetMetricsTask = new FleetMetricsTask(deps.taskManager, fetchAgents);

const router: FleetRouter = core.http.createRouter<FleetRequestHandlerContext>();
// Allow read-only users access to endpoints necessary for Integrations UI
// Only some endpoints require superuser so we pass a raw IRouter here
Expand Down Expand Up @@ -490,6 +500,7 @@ export class FleetPlugin
isProductionMode: this.isProductionMode,
kibanaVersion: this.kibanaVersion,
kibanaBranch: this.kibanaBranch,
kibanaInstanceId: this.kibanaInstanceId,
httpSetup: this.httpSetup,
cloud: this.cloud,
logger: this.logger,
Expand All @@ -504,6 +515,7 @@ export class FleetPlugin
this.fleetUsageSender?.start(plugins.taskManager);
this.checkDeletedFilesTask?.start({ taskManager: plugins.taskManager });
startFleetUsageLogger(plugins.taskManager);
this.fleetMetricsTask?.start(plugins.taskManager, core.elasticsearch.client.asInternalUser);

const logger = appContextService.getLogger();

Expand Down
6 changes: 6 additions & 0 deletions x-pack/plugins/fleet/server/services/app_context.ts
Expand Up @@ -62,6 +62,7 @@ class AppContextService {
private isProductionMode: FleetAppContext['isProductionMode'] = false;
private kibanaVersion: FleetAppContext['kibanaVersion'] = kibanaPackageJson.version;
private kibanaBranch: FleetAppContext['kibanaBranch'] = kibanaPackageJson.branch;
private kibanaInstanceId: FleetAppContext['kibanaInstanceId'] = '';
private cloud?: CloudSetup;
private logger: Logger | undefined;
private httpSetup?: HttpServiceSetup;
Expand All @@ -86,6 +87,7 @@ class AppContextService {
this.logger = appContext.logger;
this.kibanaVersion = appContext.kibanaVersion;
this.kibanaBranch = appContext.kibanaBranch;
this.kibanaInstanceId = appContext.kibanaInstanceId;
this.httpSetup = appContext.httpSetup;
this.telemetryEventsSender = appContext.telemetryEventsSender;
this.savedObjectsTagging = appContext.savedObjectsTagging;
Expand Down Expand Up @@ -209,6 +211,10 @@ class AppContextService {
return this.kibanaBranch;
}

public getKibanaInstanceId() {
return this.kibanaInstanceId;
}

public addExternalCallback(type: ExternalCallback[0], callback: ExternalCallback[1]) {
if (!this.externalCallbacks.has(type)) {
this.externalCallbacks.set(type, new Set());
Expand Down
Expand Up @@ -22,12 +22,8 @@ export function getFilteredSearchPackages() {
}

export function getFilteredInstallPackages() {
const shouldFilterFleetServer = appContextService.getConfig()?.internal?.fleetServerStandalone;
const filtered: string[] = [];
// Do not allow to install Fleet server integration if configured to use standalone fleet server
if (shouldFilterFleetServer) {
filtered.push(FLEET_SERVER_PACKAGE);
}

const excludePackages = appContextService.getConfig()?.internal?.registry?.excludePackages ?? [];

return filtered.concat(excludePackages);
Expand Down
@@ -0,0 +1,92 @@
/*
* 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 { ElasticsearchClientMock } from '@kbn/core/server/mocks';
import { coreMock } from '@kbn/core/server/mocks';
import type { CoreSetup } from '@kbn/core/server';

import { fetchAgentMetrics } from './fetch_agent_metrics';

jest.mock('../../collectors/agent_collectors', () => {
return {
getAgentUsage: jest.fn().mockResolvedValue({}),
};
});

describe('fetchAgentMetrics', () => {
const { createSetup: coreSetupMock } = coreMock;
const abortController = new AbortController();
let mockCore: CoreSetup;
let esClient: ElasticsearchClientMock;

beforeEach(async () => {
mockCore = coreSetupMock();
const [{ elasticsearch }] = await mockCore.getStartServices();
esClient = elasticsearch.client.asInternalUser as ElasticsearchClientMock;
});

it('should fetch agent metrics', async () => {
esClient.search.mockResolvedValue({
took: 5,
timed_out: false,
_shards: {
total: 1,
successful: 1,
skipped: 0,
failed: 0,
},
hits: {
total: {
value: 0,
relation: 'eq',
},
hits: [],
},
aggregations: {
versions: {
buckets: [
{
key: '8.12.0',
doc_count: 1,
},
],
},
upgrade_details: {
buckets: [
{
key: 'UPG_REQUESTED',
doc_count: 1,
},
],
},
},
});

const result = await fetchAgentMetrics(mockCore, abortController);

expect(result).toEqual({
agents: {},
agents_per_version: [
{
version: '8.12.0',
count: 1,
},
],
upgrading_step: {
downloading: 0,
extracting: 0,
failed: 0,
replacing: 0,
requested: 1,
restarting: 0,
rollback: 0,
scheduled: 0,
watching: 0,
},
});
});
});