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 3 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
100 changes: 100 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,100 @@
/*
* 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';

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 configurations = await listConfigurations({ setup });
const agentConfigValue = configurations.map((configuration) => ({
service: configuration.service,
settings: configuration.settings,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
settings: configuration.settings,
config: configuration.settings,

Here's how things look in apm-server currentlly: https://github.com/elastic/apm-server/blob/91645bde2d96a79b943c90253d62da3861ab2997/apm-server.yml#L275-L297

  #---------------------------- APM Server - Agent Configuration ----------------------------
  # Directly specify agent configuration. If `agent_config` is set, agent
  # configuration under `kibana` will be ignored.
  # An agent's incoming configuration request will be matched to an
  # agent_config with the following precedence:
  # - service.name and service.environment match an agent_config
  # - service.name matches an agent_config, service.environment == ""
  # - service.environment matches an agent_config, service.name == ""
  # - an agent_config without a name or environment set
  # An empty result is is returned if no matching result is found.
  # agent_config:
  # - service.name: ten_percent
  #   service.environment: production
  #   config:
  #     capture_body: off
  #     capture_body: true
  #     log_level: info
  #     recording: true
  #     transaction_sample_rate: 0.1
  # - service.name: frontend
  #   agent.name: rum-js
  #   config:
  #     transaction_sample_rate: 0.1

We could alternatively change the apm-server implementation if you/anyone feels strongly about calling this "settings".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm ok changing it to conform with apm-server's implementation. thanks!

}));
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}));
etag: configuration.etag,
'agent.name': configuration.agent_name,
}));

By passing the etag value down, apm-server will (later) be able to use it when recording which config has been applied by agents. The UI can then match this up to the config blocks it knows about.

We also need agent.name for restricting config for untrusted agents (RUM and Jaeger)

packagePolicy.inputs[0].config = {
agent_config: {
value: agentConfigValue,
},
};
return packagePolicy;
};

fleetPluginStart.registerExternalCallback(callbackName, callbackFn);
}
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';

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 agentConfigs = agentConfigurations.map((config) => ({
service: config.service,
settings: config.settings,
}));
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
packagePolicy.inputs[0].config = {
agent_config: {
value: agentConfigs,
},
};
return fleetPluginStart.packagePolicyService.update(
savedObjectsClient,
esClient,
id,
packagePolicy
);
})
);
}
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
14 changes: 13 additions & 1 deletion 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 @@ -114,7 +115,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 +143,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" }
]
}