Skip to content

Commit

Permalink
[APM] sourcemap routes
Browse files Browse the repository at this point in the history
  • Loading branch information
axw committed Mar 18, 2021
1 parent d02169e commit e529752
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 4 deletions.
3 changes: 2 additions & 1 deletion x-pack/plugins/apm/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"security",
"ml",
"home",
"maps"
"maps",
"fleet"
],
"server": true,
"ui": true,
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/apm/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { CloudSetup } from '../../cloud/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';
import { LicensingPluginSetup } from '../../licensing/server';
import { MlPluginSetup } from '../../ml/server';
import { FleetSetupContract, FleetStartContract } from '../../fleet/server';
import { ObservabilityPluginSetup } from '../../observability/server';
import { SecurityPluginSetup } from '../../security/server';
import { TaskManagerSetupContract } from '../../task_manager/server';
Expand Down Expand Up @@ -77,6 +78,7 @@ export class APMPlugin implements Plugin<APMPluginSetup> {
features: FeaturesPluginSetup;
security?: SecurityPluginSetup;
ml?: MlPluginSetup;
fleet?: FleetSetupContract;
}
) {
this.logger = this.initContext.logger.get();
Expand Down Expand Up @@ -152,6 +154,7 @@ export class APMPlugin implements Plugin<APMPluginSetup> {
observability: plugins.observability,
security: plugins.security,
ml: plugins.ml,
fleet: plugins.fleet,
},
});

Expand Down
12 changes: 11 additions & 1 deletion x-pack/plugins/apm/server/routes/create_apm_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ import {
transactionErrorCountChartPreview,
transactionDurationChartPreview,
} from './alerts/chart_preview';
import {
deleteSourceMapRoute,
listSourceMapsRoute,
uploadSourceMapRoute,
} from './sourcemaps';

const createApmApi = () => {
const api = createApi()
Expand Down Expand Up @@ -214,7 +219,12 @@ const createApmApi = () => {
// Alerting
.add(transactionErrorCountChartPreview)
.add(transactionDurationChartPreview)
.add(transactionErrorRateChartPreview);
.add(transactionErrorRateChartPreview)

// Source maps
.add(deleteSourceMapRoute)
.add(listSourceMapsRoute)
.add(uploadSourceMapRoute);

return api;
};
Expand Down
118 changes: 118 additions & 0 deletions x-pack/plugins/apm/server/routes/sourcemaps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* 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 * as t from 'io-ts';
import { createStaticIndexPattern } from '../lib/index_pattern/create_static_index_pattern';
import { createRoute } from './create_route';
import { setupRequest } from '../lib/helpers/setup_request';
import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client';
import { getApmIndexPatternTitle } from '../lib/index_pattern/get_apm_index_pattern_title';
import { getDynamicIndexPattern } from '../lib/index_pattern/get_dynamic_index_pattern';
import { FleetArtifactsClient } from '../../../fleet/server/services/artifacts';

async function updateFleetPolicies(packagePolicyService, savedObjectsClient, elasticsearchClient, logger) {
const client = new FleetArtifactsClient(elasticsearchClient.asInternalUser, "apm");
// TODO(axw) iterate through all pages of artifacts.
// TODO(axw) only record the minimally required artifact fields in the policies.
const artifacts = await client.listArtifacts("type:sourcemap");
const policies = await packagePolicyService.list(savedObjectsClient, {
// TODO(axw) iterate through all pages. Unlikely to be more than a handful,
// if even more than one, but better to be on the safe side.
page: 1,
perPage: 20,
kuery: 'ingest-package-policies.package.name:apm'
});
for (let item of policies.items) {
const { id, revision, updated_at, updated_by, ...policy } = item;
policy.inputs[0].config = {
sourcemap_artifacts: {
value: artifacts,
},
};
await packagePolicyService.update(savedObjectsClient, elasticsearchClient, id, policy);
}
}

export const listSourceMapsRoute = createRoute((core) => ({
endpoint: 'GET /api/apm/sourcemaps',
options: {tags: ['access:apm']},
handler: async ({ context, request }) => {
// TODO(axw) add query params to support pagination
const client = new FleetArtifactsClient(context.core.elasticsearch.client.asInternalUser, "apm");
return client.listArtifacts("type: sourcemap");
},
}));

export const deleteSourceMapRoute = createRoute((core) => ({
endpoint: 'DELETE /api/apm/sourcemaps/{id}',
options: {tags: ['access:apm']},
params: t.type({
path: t.type({
id: t.string,
}),
}),
handler: async ({ context, request }) => {
// TODO(axw) there's no guarantee the ID refers to a sourcemap artifact.
// We should investigate adding a kuery param to the deleteArtifact method,
// to require that.
const client = new FleetArtifactsClient(context.core.elasticsearch.client.asInternalUser, "apm");
await client.deleteArtifact(context.params.path.id);
await updateFleetPolicies(
context.plugins.fleet.packagePolicyService,
context.core.savedObjects.client,
context.core.elasticsearch.client,
context.logger,
);
return undefined;
},
}));

export const uploadSourceMapRoute = createRoute((core) => ({
endpoint: 'POST /api/apm/sourcemaps/{serviceName}/{serviceVersion}/{bundleFilepath}',
options: {tags: ['access:apm']},
params: t.type({
path: t.type({
serviceName: t.string,
serviceVersion: t.string,
bundleFilepath: t.string,
}),
body: t.intersection([
t.type({
version: t.number,
sources: t.array(t.string),
mappings: t.string,
names: t.array(t.string),
}),
t.partial({
file: t.string,
sourceRoot: t.string,
sourcesContent: t.array(t.string),
})
]),
}),
handler: async ({ context, request }) => {
const pathParams = context.params.path;
const content = JSON.stringify({
service_name: pathParams.serviceName,
service_version: pathParams.serviceVersion,
bundle_filepath: pathParams.bundleFilepath,
sourcemap: context.params.body,
});
const identifier = `${pathParams.serviceName}-${pathParams.serviceVersion}-${pathParams.bundleFilepath}`;
const client = new FleetArtifactsClient(context.core.elasticsearch.client.asInternalUser, "apm");
// TODO(axw) "identifier" is not used as the document _id, and multiple artifacts with the same
// identifier are possible. It might we worth investigating if this can be changed.
const artifact = await client.createArtifact({content: content, type: "sourcemap", identifier});
await updateFleetPolicies(
context.plugins.fleet.packagePolicyService,
context.core.savedObjects.client,
context.core.elasticsearch.client,
context.logger,
);
return artifact;
},
}));
14 changes: 12 additions & 2 deletions x-pack/plugins/fleet/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ export interface FleetAppContext {
httpSetup?: HttpServiceSetup;
}

export type FleetSetupContract = void;
export interface FleetSetupContract {
packagePolicyService: typeof packagePolicyService;
createArtifactsClient: (packageName: string) => FleetArtifactsClient;
};

const allSavedObjectTypes = [
OUTPUT_SAVED_OBJECT_TYPE,
Expand Down Expand Up @@ -200,7 +203,7 @@ export class FleetPlugin
this.logger = this.initializerContext.logger.get();
}

public async setup(core: CoreSetup, deps: FleetSetupDeps) {
public async setup(core: CoreSetup, deps: FleetSetupDeps): Promise<FleetSetupContract> {
this.httpSetup = core.http;
this.licensing$ = deps.licensing.license$;
this.encryptedSavedObjectsSetup = deps.encryptedSavedObjects;
Expand Down Expand Up @@ -288,6 +291,13 @@ export class FleetPlugin
}
}
}

return {
packagePolicyService,
createArtifactsClient(packageName: string) {
return new FleetArtifactsClient(core.elasticsearch.client.asInternalUser, packageName);
},
};
}

public async start(core: CoreStart, plugins: FleetStartDeps): Promise<FleetStartContract> {
Expand Down

0 comments on commit e529752

Please sign in to comment.