Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7e1ebf3
Adding android upload map validation
LikeTheSalad Jul 5, 2023
fb4002b
Adding same service name and version android map files
LikeTheSalad Jul 5, 2023
a995b4f
Setting android upload map endpoint body max size to 30 MB
LikeTheSalad Jul 5, 2023
f670716
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Jul 5, 2023
6ea4d39
Fixing lint issues
LikeTheSalad Jul 5, 2023
91e0b8b
Merge branch 'main' into uploading-android-map-files
LikeTheSalad Jul 5, 2023
271b757
Fixing type issues
LikeTheSalad Jul 5, 2023
e05b441
Adding | undefined to test return type
LikeTheSalad Jul 6, 2023
4366939
Update x-pack/plugins/apm/server/routes/source_maps/route.ts
LikeTheSalad Jul 6, 2023
5dceb1c
Merge branch 'uploading-android-map-files' of github.com:LikeTheSalad…
LikeTheSalad Jul 6, 2023
1eaef79
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jul 6, 2023
a593a12
Fixing maxBytes value for sourcemaps upload endpoint
LikeTheSalad Jul 6, 2023
03bbf95
Merge branch 'uploading-android-map-files' of github.com:LikeTheSalad…
LikeTheSalad Jul 6, 2023
588fa1a
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 6, 2023
20a0e32
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 7, 2023
740007b
Setting the sourcemaps and androidmaps endpoints max limit file size …
LikeTheSalad Jul 10, 2023
13a1def
Adding documentation for the new android upload map endpoint
LikeTheSalad Jul 10, 2023
db098dd
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 10, 2023
65052a0
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
2d6bb59
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
8ba1518
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
b285717
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
356450f
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
1b48fff
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
74bf995
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
82a1c13
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
7dc2d44
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
04ad479
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
673eb18
Update docs/apm/api.asciidoc
LikeTheSalad Jul 11, 2023
04dae25
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 24, 2023
7c34a1a
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 24, 2023
c774db8
Update x-pack/plugins/apm/server/routes/source_maps/route.ts
LikeTheSalad Jul 31, 2023
292b9ec
Removing unnecessary string conversion
LikeTheSalad Jul 31, 2023
9bccc1d
Merge branch 'main' into uploading-android-map-files
kibanamachine Jul 31, 2023
0676b19
Fixing typo
LikeTheSalad Jul 31, 2023
083ed70
Merge branch 'uploading-android-map-files' of github.com:LikeTheSalad…
LikeTheSalad Jul 31, 2023
37c7143
Renaming var to match type's name
LikeTheSalad Jul 31, 2023
4d6160c
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jul 31, 2023
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
25 changes: 24 additions & 1 deletion x-pack/plugins/apm/server/routes/fleet/source_maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ import { getPackagePolicyWithSourceMap } from './get_package_policy_decorators';

const doUnzip = promisify(unzip);

interface ApmSourceMapArtifactBody {
interface ApmMapArtifactBody {
serviceName: string;
serviceVersion: string;
bundleFilepath: string;
sourceMap: string;
}

interface ApmSourceMapArtifactBody
extends Omit<ApmMapArtifactBody, 'sourceMap'> {
sourceMap: SourceMap;
}

export type ArtifactSourceMap = Omit<Artifact, 'body'> & {
body: ApmSourceMapArtifactBody;
};
Expand Down Expand Up @@ -104,6 +110,23 @@ export async function createFleetSourceMapArtifact({
});
}

export async function createFleetAndroidMapArtifact({
apmArtifactBody,
fleetPluginStart,
}: {
apmArtifactBody: ApmMapArtifactBody;
fleetPluginStart: FleetPluginStart;
}) {
const apmArtifactClient = getApmArtifactClient(fleetPluginStart);
const identifier = `${apmArtifactBody.serviceName}-${apmArtifactBody.serviceVersion}-android`;

return apmArtifactClient.createArtifact({
type: 'sourcemap',
identifier,
content: JSON.stringify(apmArtifactBody),
});
}

export async function deleteFleetSourcemapArtifact({
id,
fleetPluginStart,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Artifact } from '@kbn/fleet-plugin/server';
import { getUnzippedArtifactBody } from '../fleet/source_maps';
import { APM_SOURCE_MAP_INDEX } from '../settings/apm_indices/get_apm_indices';
import { ApmSourceMap } from './create_apm_source_map_index_template';
import { getEncodedContent, getSourceMapId } from './sourcemap_utils';
import { getEncodedSourceMapContent, getSourceMapId } from './sourcemap_utils';

export async function bulkCreateApmSourceMaps({
artifacts,
Expand All @@ -24,7 +24,7 @@ export async function bulkCreateApmSourceMaps({
const { serviceName, serviceVersion, bundleFilepath, sourceMap } =
await getUnzippedArtifactBody(artifact.body);

const { contentEncoded, contentHash } = await getEncodedContent(
const { contentEncoded, contentHash } = await getEncodedSourceMapContent(
sourceMap
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { Logger } from '@kbn/core/server';
import { APM_SOURCE_MAP_INDEX } from '../settings/apm_indices/get_apm_indices';
import { ApmSourceMap } from './create_apm_source_map_index_template';
import { SourceMap } from './route';
import { getEncodedContent, getSourceMapId } from './sourcemap_utils';
import {
getEncodedSourceMapContent,
getEncodedContent,
getSourceMapId,
} from './sourcemap_utils';

export async function createApmSourceMap({
internalESClient,
Expand All @@ -31,9 +35,75 @@ export async function createApmSourceMap({
serviceName: string;
serviceVersion: string;
}) {
const { contentEncoded, contentHash } = await getEncodedContent(
const { contentEncoded, contentHash } = await getEncodedSourceMapContent(
sourceMapContent
);
return await doCreateApmMap({
internalESClient,
logger,
fleetId,
created,
bundleFilepath,
serviceName,
serviceVersion,
contentEncoded,
contentHash,
});
}

export async function createApmAndroidMap({
internalESClient,
logger,
fleetId,
created,
mapContent,
bundleFilepath,
serviceName,
serviceVersion,
}: {
internalESClient: ElasticsearchClient;
logger: Logger;
fleetId: string;
created: string;
mapContent: string;
bundleFilepath: string;
serviceName: string;
serviceVersion: string;
}) {
const { contentEncoded, contentHash } = await getEncodedContent(mapContent);
return await doCreateApmMap({
internalESClient,
logger,
fleetId,
created,
bundleFilepath,
serviceName,
serviceVersion,
contentEncoded,
contentHash,
});
}
async function doCreateApmMap({
internalESClient,
logger,
fleetId,
created,
bundleFilepath,
serviceName,
serviceVersion,
contentEncoded,
contentHash,
}: {
internalESClient: ElasticsearchClient;
logger: Logger;
fleetId: string;
created: string;
bundleFilepath: string;
serviceName: string;
serviceVersion: string;
contentEncoded: string;
contentHash: string;
}) {
const doc: ApmSourceMap = {
fleet_id: fleetId,
created,
Expand Down
105 changes: 104 additions & 1 deletion x-pack/plugins/apm/server/routes/source_maps/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ import { SavedObjectsClientContract } from '@kbn/core/server';
import { Artifact } from '@kbn/fleet-plugin/server';
import { jsonRt, toNumberRt } from '@kbn/io-ts-utils';
import * as t from 'io-ts';
import { either } from 'fp-ts/lib/Either';
import { ApmFeatureFlags } from '../../../common/apm_feature_flags';
import { getInternalSavedObjectsClient } from '../../lib/helpers/get_internal_saved_objects_client';
import { stringFromBufferRt } from '../../utils/string_from_buffer_rt';
import { createApmServerRoute } from '../apm_routes/create_apm_server_route';
import {
createFleetSourceMapArtifact,
createFleetAndroidMapArtifact,
deleteFleetSourcemapArtifact,
getCleanedBundleFilePath,
listSourceMapArtifacts,
ListSourceMapArtifactsResponse,
updateSourceMapsOnFleetPolicies,
} from '../fleet/source_maps';
import { createApmSourceMap } from './create_apm_source_map';
import {
createApmSourceMap,
createApmAndroidMap,
} from './create_apm_source_map';
import { deleteApmSourceMap } from './delete_apm_sourcemap';
import { runFleetSourcemapArtifactsMigration } from './schedule_source_map_migration';

Expand All @@ -41,6 +46,24 @@ export const sourceMapRt = t.intersection([

export type SourceMap = t.TypeOf<typeof sourceMapRt>;

const androidMapValidation = new t.Type<string, string, unknown>(
'ANDROID_MAP_VALIDATION',
t.string.is,
(input, context): t.Validation<string> =>
either.chain(
t.string.validate(input, context),
(str): t.Validation<string> => {
const firstLine = str.split('\n', 1)[0];
if (firstLine.trim() === '# compiler: R8') {
return t.success(str);
} else {
return t.failure(input, context);
}
}
),
(a): string => a
);

function throwNotImplementedIfSourceMapNotAvailable(
featureFlags: ApmFeatureFlags
): void {
Expand Down Expand Up @@ -169,6 +192,85 @@ const uploadSourceMapRoute = createApmServerRoute({
},
});

const uploadAndroidMapRoute = createApmServerRoute({
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please add docs for this one? (Example: https://www.elastic.co/guide/en/kibana/current/apm-api.html)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @gbamparop - I've just added docs for the overall Android source map API. There are lots of similarities with the RUM one, which is why I took a lot of the parts involved in that part of the doc file. I'm not sure if my approach is the right one, so please have a look just in case there might be a better way to handle it. I'm also looping @bmorelli25 in, in case he's got some inputs as well.

endpoint: 'POST /api/apm/androidmaps 2023-10-31',
options: {
tags: ['access:apm', 'access:apm_write'],
body: { accepts: ['multipart/form-data'], maxBytes: 30 * 1024 * 1024 },
},
params: t.type({
body: t.type({
service_name: t.string,
service_version: t.string,
map_file: t
.union([t.string, stringFromBufferRt])
.pipe(androidMapValidation),
}),
}),
handler: async ({
params,
plugins,
core,
logger,
featureFlags,
}): Promise<Artifact | undefined> => {
throwNotImplementedIfSourceMapNotAvailable(featureFlags);

const {
service_name: serviceName,
service_version: serviceVersion,
map_file: sourceMapContent,
} = params.body;
const bundleFilePath = 'android';
const fleetPluginStart = await plugins.fleet?.start();
const coreStart = await core.start();
const internalESClient = coreStart.elasticsearch.client.asInternalUser;
const savedObjectsClient = await getInternalSavedObjectsClient(coreStart);
try {
if (fleetPluginStart) {
// create source map as fleet artifact
const artifact = await createFleetAndroidMapArtifact({
fleetPluginStart,
apmArtifactBody: {
serviceName,
serviceVersion,
bundleFilepath: bundleFilePath,
sourceMap: sourceMapContent,
},
});

// sync source map to APM managed index
await createApmAndroidMap({
internalESClient,
logger,
fleetId: artifact.id,
created: artifact.created,
mapContent: sourceMapContent,
bundleFilepath: bundleFilePath.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

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

bundleFilePath is a string. Is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, I think it's not needed. I've just removed it 👍

serviceName,
serviceVersion,
});

// sync source map to fleet policy
await updateSourceMapsOnFleetPolicies({
coreStart,
fleetPluginStart,
savedObjectsClient:
savedObjectsClient as unknown as SavedObjectsClientContract,
internalESClient,
});

return artifact;
}
} catch (e) {
throw Boom.internal(
'Something went wrong while creating a new android map',
e
);
}
},
});

const deleteSourceMapRoute = createApmServerRoute({
endpoint: 'DELETE /api/apm/sourcemaps/{id} 2023-10-31',
options: { tags: ['access:apm', 'access:apm_write'] },
Expand Down Expand Up @@ -230,5 +332,6 @@ export const sourceMapsRouteRepository = {
...listSourceMapRoute,
...uploadSourceMapRoute,
...deleteSourceMapRoute,
...uploadAndroidMapRoute,
...migrateFleetArtifactsSourceMapRoute,
};
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ function asSha256Encoded(content: BinaryLike): string {
return createHash('sha256').update(content).digest('hex');
}

export async function getEncodedContent(sourceMapContent: SourceMap) {
const contentBuffer = Buffer.from(JSON.stringify(sourceMapContent));
export async function getEncodedSourceMapContent(sourceMapContent: SourceMap) {
return getEncodedContent(JSON.stringify(sourceMapContent));
}
export async function getEncodedContent(textContent: string) {
const contentBuffer = Buffer.from(textContent);
const contentZipped = await deflateAsync(contentBuffer);
const contentEncoded = contentZipped.toString('base64');
const contentHash = asSha256Encoded(contentZipped);
Expand Down
Loading