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

[APM] Syncs agent config settings to APM Fleet policies #100744

Merged
merged 13 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion x-pack/plugins/apm/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"security",
"ml",
"home",
"maps"
"maps",
"fleet"
],
"server": true,
"ui": true,
Expand Down
30 changes: 30 additions & 0 deletions x-pack/plugins/apm/server/lib/fleet/get_apm_package_policies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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 {
CoreSetup,
CoreStart,
SavedObjectsClientContract,
} from 'kibana/server';
import { APMPluginStartDependencies } from '../../types';
import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';

export async function getApmPackgePolicies({
core,
fleetPluginStart,
}: {
core: { setup: CoreSetup; start: () => Promise<CoreStart> };
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
}) {
// @ts-ignore
const savedObjectsClient: SavedObjectsClientContract = await getInternalSavedObjectsClient(
core.setup
);
return await fleetPluginStart.packagePolicyService.list(savedObjectsClient, {
kuery: 'ingest-package-policies.package.name:apm',
});
}
134 changes: 134 additions & 0 deletions x-pack/plugins/apm/server/lib/fleet/register_fleet_policy_callbacks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* 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 { APMPlugin, APMRouteHandlerResources } from '../..';
import { listConfigurations } from '../settings/agent_configuration/list_configurations';
import { setupRequest } from '../helpers/setup_request';
import { APMPluginStartDependencies } from '../../types';
import { ExternalCallback } from '../../../../fleet/server';
import { AGENT_NAME } from '../../../common/elasticsearch_fieldnames';
import { AgentConfiguration } from '../../../common/agent_configuration/configuration_types';

export async function registerFleetPolicyCallbacks({
plugins,
ruleDataClient,
config,
logger,
}: {
plugins: APMRouteHandlerResources['plugins'];
ruleDataClient: APMRouteHandlerResources['ruleDataClient'];
config: NonNullable<APMPlugin['currentConfig']>;
logger: NonNullable<APMPlugin['logger']>;
}) {
if (!plugins.fleet) {
return;
}
const fleetPluginStart = await plugins.fleet.start();

// Registers a callback invoked when a policy is created to populate the APM
// integration policy with pre-existing agent configurations
registerAgentConfigExternalCallback({
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved
fleetPluginStart,
callbackName: 'packagePolicyCreate',
cauemarcondes marked this conversation as resolved.
Show resolved Hide resolved
plugins,
ruleDataClient,
config,
logger,
});

// Registers a callback invoked when a policy is updated to populate the APM
// integration policy with existing agent configurations
registerAgentConfigExternalCallback({
fleetPluginStart,
callbackName: 'packagePolicyUpdate',
plugins,
ruleDataClient,
config,
logger,
});
}

type ExternalCallbackParams = Parameters<ExternalCallback[1]>;
type PackagePolicy = ExternalCallbackParams[0];
type Context = ExternalCallbackParams[1];
type Request = ExternalCallbackParams[2];

function registerAgentConfigExternalCallback({
fleetPluginStart,
callbackName,
plugins,
ruleDataClient,
config,
logger,
}: {
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
callbackName: ExternalCallback[0];
plugins: APMRouteHandlerResources['plugins'];
ruleDataClient: APMRouteHandlerResources['ruleDataClient'];
config: NonNullable<APMPlugin['currentConfig']>;
logger: NonNullable<APMPlugin['logger']>;
}) {
const callbackFn: ExternalCallback[1] = async (
packagePolicy: PackagePolicy,
context: Context,
request: Request
) => {
if (packagePolicy.package?.name !== 'apm') {
return packagePolicy;
}
const setup = await setupRequest({
context: context as any,
params: { query: { _inspect: false } },
core: null as any,
plugins,
request,
config,
logger,
ruleDataClient,
});
const agentConfigurations = await listConfigurations({ setup });
return getPackagePolicyWithAgentConfigurations(
packagePolicy,
agentConfigurations
);
};

fleetPluginStart.registerExternalCallback(callbackName, callbackFn);
}

const APM_SERVER = 'apm-server';

// Immutable function applies the given package policy with a set of agent configurations
export function getPackagePolicyWithAgentConfigurations(
packagePolicy: PackagePolicy,
agentConfigurations: AgentConfiguration[]
) {
const [firstInput, ...restInputs] = packagePolicy.inputs;
const apmServerValue = firstInput?.config?.[APM_SERVER].value;
return {
...packagePolicy,
inputs: [
{
...firstInput,
config: {
[APM_SERVER]: {
Comment on lines +117 to +118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ogupte Don't you need to add the others config here to avoid overwriting the entire config object?

Suggested change
config: {
[APM_SERVER]: {
config: {
...firstInput.config,
[APM_SERVER]: {

value: {
...apmServerValue,
agent_config: agentConfigurations.map((configuration) => ({
service: configuration.service,
config: configuration.settings,
etag: configuration.etag,
[AGENT_NAME]: configuration.agent_name,
})),
},
},
},
},
...restInputs,
],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 {
CoreSetup,
CoreStart,
SavedObjectsClientContract,
} from 'kibana/server';
import { APMPluginStartDependencies } from '../../types';
import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client';
import { Setup } from '../helpers/setup_request';
import { listConfigurations } from '../settings/agent_configuration/list_configurations';
import { getApmPackgePolicies } from './get_apm_package_policies';
import { getPackagePolicyWithAgentConfigurations } from './register_fleet_policy_callbacks';

export async function syncAgentConfigsToApmPackagePolicies({
core,
fleetPluginStart,
setup,
}: {
core: { setup: CoreSetup; start: () => Promise<CoreStart> };
fleetPluginStart: NonNullable<APMPluginStartDependencies['fleet']>;
setup: Setup;
}) {
const coreStart = await core.start();
const esClient = coreStart.elasticsearch.client.asInternalUser;
const [
savedObjectsClient,
agentConfigurations,
packagePolicies,
] = await Promise.all([
getInternalSavedObjectsClient(core.setup),
listConfigurations({ setup }),
getApmPackgePolicies({
core,
fleetPluginStart,
}),
]);

return Promise.all(
packagePolicies.items.map(async (item) => {
const { id, revision, updated_at, updated_by, ...packagePolicy } = item; // eslint-disable-line @typescript-eslint/naming-convention
const updatedPackagePolicy = getPackagePolicyWithAgentConfigurations(
packagePolicy,
agentConfigurations
);
return fleetPluginStart.packagePolicyService.update(
(savedObjectsClient as unknown) as SavedObjectsClientContract,
esClient,
id,
updatedPackagePolicy
);
})
);
}
34 changes: 22 additions & 12 deletions x-pack/plugins/apm/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { mergeConfigs } from './index';
import { UI_SETTINGS } from '../../../../src/plugins/data/common';
import { APM_FEATURE, registerFeaturesUsage } from './feature';
import { registerApmAlerts } from './lib/alerts/register_apm_alerts';
import { registerFleetPolicyCallbacks } from './lib/fleet/register_fleet_policy_callbacks';
import { createApmTelemetry } from './lib/apm_telemetry';
import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client';
import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_objects_client';
Expand Down Expand Up @@ -186,6 +187,19 @@ export class APMPlugin
ready,
});

const resourcePlugins = mapValues(plugins, (value, key) => {
return {
setup: value,
start: () =>
core.getStartServices().then((services) => {
const [, pluginsStartContracts] = services;
return pluginsStartContracts[
key as keyof APMPluginStartDependencies
];
}),
};
}) as APMRouteHandlerResources['plugins'];

registerRoutes({
core: {
setup: core,
Expand All @@ -195,18 +209,7 @@ export class APMPlugin
config: currentConfig,
repository: getGlobalApmServerRouteRepository(),
ruleDataClient,
plugins: mapValues(plugins, (value, key) => {
return {
setup: value,
start: () =>
core.getStartServices().then((services) => {
const [, pluginsStartContracts] = services;
return pluginsStartContracts[
key as keyof APMPluginStartDependencies
];
}),
};
}) as APMRouteHandlerResources['plugins'],
plugins: resourcePlugins,
});

const boundGetApmIndices = async () =>
Expand All @@ -225,6 +228,13 @@ export class APMPlugin
});
}

registerFleetPolicyCallbacks({
plugins: resourcePlugins,
ruleDataClient,
config: this.currentConfig,
logger: this.logger,
});

return {
config$: mergedConfig$,
getApmIndices: boundGetApmIndices,
Expand Down
31 changes: 28 additions & 3 deletions x-pack/plugins/apm/server/routes/settings/agent_configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from '../../../common/agent_configuration/runtime_types/agent_configuration_intake_rt';
import { getSearchAggregatedTransactions } from '../../lib/helpers/aggregated_transactions';
import { createApmServerRouteRepository } from '../create_apm_server_route_repository';
import { syncAgentConfigsToApmPackagePolicies } from '../../lib/fleet/sync_agent_configs_to_apm_package_policies';

// get list of configurations
const agentConfigurationRoute = createApmServerRoute({
Expand Down Expand Up @@ -78,7 +79,7 @@ const deleteAgentConfigurationRoute = createApmServerRoute({
}),
handler: async (resources) => {
const setup = await setupRequest(resources);
const { params, logger } = resources;
const { params, logger, core } = resources;

const { service } = params.body;

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

return await deleteConfiguration({
const deleteConfigurationResult = await deleteConfiguration({
configurationId: config._id,
setup,
});

if (resources.plugins.fleet) {
await syncAgentConfigsToApmPackagePolicies({
core,
fleetPluginStart: await resources.plugins.fleet.start(),
setup,
});
logger.info(
`Updated Fleet integration policy for APM to remove the deleted agent configuration.`
);
}

return deleteConfigurationResult;
},
});

Expand All @@ -114,7 +128,7 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
]),
handler: async (resources) => {
const setup = await setupRequest(resources);
const { params, logger } = resources;
const { params, logger, core } = resources;
const { body, query } = params;

// if the config already exists, it is fetched and updated
Expand Down Expand Up @@ -142,6 +156,17 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({
configurationIntake: body,
setup,
});

if (resources.plugins.fleet) {
await syncAgentConfigsToApmPackagePolicies({
core,
fleetPluginStart: await resources.plugins.fleet.start(),
setup,
});
logger.info(
`Saved latest agent settings to Fleet integration policy for APM.`
);
}
},
});

Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/apm/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ import {
TaskManagerSetupContract,
TaskManagerStartContract,
} from '../../task_manager/server';
import {
FleetSetupContract as FleetPluginSetup,
FleetStartContract as FleetPluginStart,
} from '../../fleet/server';
import { APMConfig } from '.';
import { getApmIndices } from './lib/settings/apm_indices/get_apm_indices';
import { createApmEventClient } from './lib/helpers/create_es_client/create_apm_event_client';
Expand Down Expand Up @@ -123,6 +127,10 @@ interface DependencyMap {
setup: RuleRegistryPluginSetupContract;
start: RuleRegistryPluginStartContract;
};
fleet: {
setup: FleetPluginSetup;
start: FleetPluginStart;
};
}

const requiredDependencies = [
Expand All @@ -148,6 +156,7 @@ const optionalDependencies = [
'ml',
'home',
'maps',
'fleet',
] as const;

type RequiredDependencies = Pick<
Expand Down
Loading