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 5 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
122 changes: 122 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,122 @@
/*
* 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();

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,
});
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);
}

export function getPackagePolicyWithAgentConfigurations(
packagePolicy: PackagePolicy,
agentConfigurations: AgentConfiguration[]
) {
const [firstInput, ...restInputs] = packagePolicy.inputs;
return {
...packagePolicy,
inputs: [
{
...firstInput,
config: {
agent_config: {
value: 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,55 @@
/*
* 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 { 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;
// @ts-ignore
const savedObjectsClient: SavedObjectsClientContract = await getInternalSavedObjectsClient(
core.setup
);
const agentConfigurations = await listConfigurations({ setup });
const packagePolicies = await fleetPluginStart.packagePolicyService.list(
savedObjectsClient,
{ kuery: 'ingest-package-policies.package.name:apm' }
);

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,
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 @@ -25,6 +25,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 @@ -196,6 +197,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 @@ -205,18 +219,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 @@ -235,6 +238,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(
`Saved latest agent settings to Fleet integration policy for APM.`
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you're logging something, maybe would be good to have a specific message when it is deleting?

);
}

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
3 changes: 2 additions & 1 deletion x-pack/plugins/apm/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
{ "path": "../rule_registry/tsconfig.json" },
{ "path": "../security/tsconfig.json" },
{ "path": "../task_manager/tsconfig.json" },
{ "path": "../triggers_actions_ui/tsconfig.json" }
{ "path": "../triggers_actions_ui/tsconfig.json" },
{ "path": "../fleet/tsconfig.json" }
]
}