Skip to content

Commit fdc4c13

Browse files
oguptekibanamachine
andcommitted
[APM] Syncs agent config settings to APM Fleet policies (#100744)
* [APM] Syncs agent config settings to APM Fleet policies (#95501) * fixes eslint issues * fixes malformed line comment * - consolidated logic that applies agent configurations to package policy objects - update package policy agent_configs to include etag, agent.name, and change settings -> config * Synchronizes agent configs whenever configuration is deleted. * PR feedback * nest agent_config within `apm-server` in the package policy input * nests agent_config under the requried 'value' property of config['apm-server'] in order to pass validation checks * - externalizes getApmPackagePolicies for reusability - parallelizes operations for improved performance Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
1 parent 5d5194c commit fdc4c13

8 files changed

Lines changed: 286 additions & 17 deletions

File tree

x-pack/plugins/apm/kibana.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
"security",
2424
"ml",
2525
"home",
26-
"maps"
26+
"maps",
27+
"fleet"
2728
],
2829
"server": true,
2930
"ui": true,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
CoreSetup,
10+
CoreStart,
11+
SavedObjectsClientContract,
12+
} from 'kibana/server';
13+
import { APMPluginStartDependencies } from '../../types';
14+
import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
15+
16+
export async function getApmPackgePolicies({
17+
core,
18+
fleetPluginStart,
19+
}: {
20+
core: { setup: CoreSetup; start: () => Promise<CoreStart> };
21+
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
22+
}) {
23+
// @ts-ignore
24+
const savedObjectsClient: SavedObjectsClientContract = await getInternalSavedObjectsClient(
25+
core.setup
26+
);
27+
return await fleetPluginStart.packagePolicyService.list(savedObjectsClient, {
28+
kuery: 'ingest-package-policies.package.name:apm',
29+
});
30+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { APMPlugin, APMRouteHandlerResources } from '../..';
9+
import { listConfigurations } from '../settings/agent_configuration/list_configurations';
10+
import { setupRequest } from '../helpers/setup_request';
11+
import { APMPluginStartDependencies } from '../../types';
12+
import { ExternalCallback } from '../../../../fleet/server';
13+
import { AGENT_NAME } from '../../../common/elasticsearch_fieldnames';
14+
import { AgentConfiguration } from '../../../common/agent_configuration/configuration_types';
15+
16+
export async function registerFleetPolicyCallbacks({
17+
plugins,
18+
ruleDataClient,
19+
config,
20+
logger,
21+
}: {
22+
plugins: APMRouteHandlerResources['plugins'];
23+
ruleDataClient: APMRouteHandlerResources['ruleDataClient'];
24+
config: NonNullable<APMPlugin['currentConfig']>;
25+
logger: NonNullable<APMPlugin['logger']>;
26+
}) {
27+
if (!plugins.fleet) {
28+
return;
29+
}
30+
const fleetPluginStart = await plugins.fleet.start();
31+
32+
// Registers a callback invoked when a policy is created to populate the APM
33+
// integration policy with pre-existing agent configurations
34+
registerAgentConfigExternalCallback({
35+
fleetPluginStart,
36+
callbackName: 'packagePolicyCreate',
37+
plugins,
38+
ruleDataClient,
39+
config,
40+
logger,
41+
});
42+
43+
// Registers a callback invoked when a policy is updated to populate the APM
44+
// integration policy with existing agent configurations
45+
registerAgentConfigExternalCallback({
46+
fleetPluginStart,
47+
callbackName: 'packagePolicyUpdate',
48+
plugins,
49+
ruleDataClient,
50+
config,
51+
logger,
52+
});
53+
}
54+
55+
type ExternalCallbackParams = Parameters<ExternalCallback[1]>;
56+
type PackagePolicy = ExternalCallbackParams[0];
57+
type Context = ExternalCallbackParams[1];
58+
type Request = ExternalCallbackParams[2];
59+
60+
function registerAgentConfigExternalCallback({
61+
fleetPluginStart,
62+
callbackName,
63+
plugins,
64+
ruleDataClient,
65+
config,
66+
logger,
67+
}: {
68+
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
69+
callbackName: ExternalCallback[0];
70+
plugins: APMRouteHandlerResources['plugins'];
71+
ruleDataClient: APMRouteHandlerResources['ruleDataClient'];
72+
config: NonNullable<APMPlugin['currentConfig']>;
73+
logger: NonNullable<APMPlugin['logger']>;
74+
}) {
75+
const callbackFn: ExternalCallback[1] = async (
76+
packagePolicy: PackagePolicy,
77+
context: Context,
78+
request: Request
79+
) => {
80+
if (packagePolicy.package?.name !== 'apm') {
81+
return packagePolicy;
82+
}
83+
const setup = await setupRequest({
84+
context: context as any,
85+
params: { query: { _inspect: false } },
86+
core: null as any,
87+
plugins,
88+
request,
89+
config,
90+
logger,
91+
ruleDataClient,
92+
});
93+
const agentConfigurations = await listConfigurations({ setup });
94+
return getPackagePolicyWithAgentConfigurations(
95+
packagePolicy,
96+
agentConfigurations
97+
);
98+
};
99+
100+
fleetPluginStart.registerExternalCallback(callbackName, callbackFn);
101+
}
102+
103+
const APM_SERVER = 'apm-server';
104+
105+
// Immutable function applies the given package policy with a set of agent configurations
106+
export function getPackagePolicyWithAgentConfigurations(
107+
packagePolicy: PackagePolicy,
108+
agentConfigurations: AgentConfiguration[]
109+
) {
110+
const [firstInput, ...restInputs] = packagePolicy.inputs;
111+
const apmServerValue = firstInput?.config?.[APM_SERVER].value;
112+
return {
113+
...packagePolicy,
114+
inputs: [
115+
{
116+
...firstInput,
117+
config: {
118+
[APM_SERVER]: {
119+
value: {
120+
...apmServerValue,
121+
agent_config: agentConfigurations.map((configuration) => ({
122+
service: configuration.service,
123+
config: configuration.settings,
124+
etag: configuration.etag,
125+
[AGENT_NAME]: configuration.agent_name,
126+
})),
127+
},
128+
},
129+
},
130+
},
131+
...restInputs,
132+
],
133+
};
134+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import {
9+
CoreSetup,
10+
CoreStart,
11+
SavedObjectsClientContract,
12+
} from 'kibana/server';
13+
import { APMPluginStartDependencies } from '../../types';
14+
import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
15+
import { Setup } from '../helpers/setup_request';
16+
import { listConfigurations } from '../settings/agent_configuration/list_configurations';
17+
import { getApmPackgePolicies } from './get_apm_package_policies';
18+
import { getPackagePolicyWithAgentConfigurations } from './register_fleet_policy_callbacks';
19+
20+
export async function syncAgentConfigsToApmPackagePolicies({
21+
core,
22+
fleetPluginStart,
23+
setup,
24+
}: {
25+
core: { setup: CoreSetup; start: () => Promise<CoreStart> };
26+
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
27+
setup: Setup;
28+
}) {
29+
const coreStart = await core.start();
30+
const esClient = coreStart.elasticsearch.client.asInternalUser;
31+
const [
32+
savedObjectsClient,
33+
agentConfigurations,
34+
packagePolicies,
35+
] = await Promise.all([
36+
getInternalSavedObjectsClient(core.setup),
37+
listConfigurations({ setup }),
38+
getApmPackgePolicies({
39+
core,
40+
fleetPluginStart,
41+
}),
42+
]);
43+
44+
return Promise.all(
45+
packagePolicies.items.map(async (item) => {
46+
const { id, revision, updated_at, updated_by, ...packagePolicy } = item; // eslint-disable-line @typescript-eslint/naming-convention
47+
const updatedPackagePolicy = getPackagePolicyWithAgentConfigurations(
48+
packagePolicy,
49+
agentConfigurations
50+
);
51+
return fleetPluginStart.packagePolicyService.update(
52+
(savedObjectsClient as unknown) as SavedObjectsClientContract,
53+
esClient,
54+
id,
55+
updatedPackagePolicy
56+
);
57+
})
58+
);
59+
}

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

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { mergeConfigs } from './index';
2424
import { UI_SETTINGS } from '../../../../src/plugins/data/common';
2525
import { APM_FEATURE, registerFeaturesUsage } from './feature';
2626
import { registerApmAlerts } from './lib/alerts/register_apm_alerts';
27+
import { registerFleetPolicyCallbacks } from './lib/fleet/register_fleet_policy_callbacks';
2728
import { createApmTelemetry } from './lib/apm_telemetry';
2829
import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client';
2930
import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
@@ -186,6 +187,19 @@ export class APMPlugin
186187
ready,
187188
});
188189

190+
const resourcePlugins = mapValues(plugins, (value, key) => {
191+
return {
192+
setup: value,
193+
start: () =>
194+
core.getStartServices().then((services) => {
195+
const [, pluginsStartContracts] = services;
196+
return pluginsStartContracts[
197+
key as keyof APMPluginStartDependencies
198+
];
199+
}),
200+
};
201+
}) as APMRouteHandlerResources['plugins'];
202+
189203
registerRoutes({
190204
core: {
191205
setup: core,
@@ -195,18 +209,7 @@ export class APMPlugin
195209
config: currentConfig,
196210
repository: getGlobalApmServerRouteRepository(),
197211
ruleDataClient,
198-
plugins: mapValues(plugins, (value, key) => {
199-
return {
200-
setup: value,
201-
start: () =>
202-
core.getStartServices().then((services) => {
203-
const [, pluginsStartContracts] = services;
204-
return pluginsStartContracts[
205-
key as keyof APMPluginStartDependencies
206-
];
207-
}),
208-
};
209-
}) as APMRouteHandlerResources['plugins'],
212+
plugins: resourcePlugins,
210213
});
211214

212215
const boundGetApmIndices = async () =>
@@ -225,6 +228,13 @@ export class APMPlugin
225228
});
226229
}
227230

231+
registerFleetPolicyCallbacks({
232+
plugins: resourcePlugins,
233+
ruleDataClient,
234+
config: this.currentConfig,
235+
logger: this.logger,
236+
});
237+
228238
return {
229239
config$: mergedConfig$,
230240
getApmIndices: boundGetApmIndices,

x-pack/plugins/apm/server/routes/settings/agent_configuration.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from '../../../common/agent_configuration/runtime_types/agent_configuration_intake_rt';
2626
import { getSearchAggregatedTransactions } from '../../lib/helpers/aggregated_transactions';
2727
import { createApmServerRouteRepository } from '../create_apm_server_route_repository';
28+
import { syncAgentConfigsToApmPackagePolicies } from '../../lib/fleet/sync_agent_configs_to_apm_package_policies';
2829

2930
// get list of configurations
3031
const agentConfigurationRoute = createApmServerRoute({
@@ -78,7 +79,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({
7879
}),
7980
handler: async (resources) => {
8081
const setup = await setupRequest(resources);
81-
const { params, logger } = resources;
82+
const { params, logger, core } = resources;
8283

8384
const { service } = params.body;
8485

@@ -95,10 +96,23 @@ const deleteAgentConfigurationRoute = createApmServerRoute({
9596
`Deleting config ${service.name}/${service.environment} (${config._id})`
9697
);
9798

98-
return await deleteConfiguration({
99+
const deleteConfigurationResult = await deleteConfiguration({
99100
configurationId: config._id,
100101
setup,
101102
});
103+
104+
if (resources.plugins.fleet) {
105+
await syncAgentConfigsToApmPackagePolicies({
106+
core,
107+
fleetPluginStart: await resources.plugins.fleet.start(),
108+
setup,
109+
});
110+
logger.info(
111+
`Updated Fleet integration policy for APM to remove the deleted agent configuration.`
112+
);
113+
}
114+
115+
return deleteConfigurationResult;
102116
},
103117
});
104118

@@ -114,7 +128,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
114128
]),
115129
handler: async (resources) => {
116130
const setup = await setupRequest(resources);
117-
const { params, logger } = resources;
131+
const { params, logger, core } = resources;
118132
const { body, query } = params;
119133

120134
// if the config already exists, it is fetched and updated
@@ -142,6 +156,17 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
142156
configurationIntake: body,
143157
setup,
144158
});
159+
160+
if (resources.plugins.fleet) {
161+
await syncAgentConfigsToApmPackagePolicies({
162+
core,
163+
fleetPluginStart: await resources.plugins.fleet.start(),
164+
setup,
165+
});
166+
logger.info(
167+
`Saved latest agent settings to Fleet integration policy for APM.`
168+
);
169+
}
145170
},
146171
});
147172

x-pack/plugins/apm/server/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ import {
4343
TaskManagerSetupContract,
4444
TaskManagerStartContract,
4545
} from '../../task_manager/server';
46+
import {
47+
FleetSetupContract as FleetPluginSetup,
48+
FleetStartContract as FleetPluginStart,
49+
} from '../../fleet/server';
4650
import { APMConfig } from '.';
4751
import { getApmIndices } from './lib/settings/apm_indices/get_apm_indices';
4852
import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client';
@@ -123,6 +127,10 @@ interface DependencyMap {
123127
setup: RuleRegistryPluginSetupContract;
124128
start: RuleRegistryPluginStartContract;
125129
};
130+
fleet: {
131+
setup: FleetPluginSetup;
132+
start: FleetPluginStart;
133+
};
126134
}
127135

128136
const requiredDependencies = [
@@ -148,6 +156,7 @@ const optionalDependencies = [
148156
'ml',
149157
'home',
150158
'maps',
159+
'fleet',
151160
] as const;
152161

153162
type RequiredDependencies = Pick<

0 commit comments

Comments
 (0)