Add catch around JSON trace upload for more error handling (#38520)

This commit is contained in:
Connor Lindsey 2021-08-25 10:08:46 -06:00 committed by GitHub
parent d1b6132302
commit 76ade24f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 22 deletions

View File

@ -89,6 +89,18 @@ describe('JaegerDatasource', () => {
expect(field.values.length).toBe(2);
});
it('should fail on invalid json file upload', async () => {
const ds = new JaegerDatasource(defaultSettings);
ds.uploadedJson = JSON.stringify({ key: 'value', arr: [] });
const response = await lastValueFrom(
ds.query({
targets: [{ queryType: 'upload', refId: 'A' }],
} as any)
);
expect(response.error?.message).toBeDefined();
expect(response.data.length).toBe(0);
});
it('should return search results when the query type is search', async () => {
const mock = setupFetchMock({ data: [testResponse] });
const ds = new JaegerDatasource(defaultSettings, timeSrvStub);

View File

@ -58,8 +58,13 @@ export class JaegerDatasource extends DataSourceApi<JaegerQuery> {
if (!this.uploadedJson) {
return of({ data: [] });
}
const traceData = JSON.parse(this.uploadedJson as string).data[0];
return of({ data: [createTraceFrame(traceData), ...createGraphFrames(traceData)] });
try {
const traceData = JSON.parse(this.uploadedJson as string).data[0];
return of({ data: [createTraceFrame(traceData), ...createGraphFrames(traceData)] });
} catch (error) {
return of({ error: { message: 'JSON is not valid Jaeger format' }, data: [] });
}
}
let jaegerQuery = pick(target, ['operation', 'service', 'tags', 'minDuration', 'maxDuration', 'limit']);

View File

@ -119,6 +119,18 @@ describe('Tempo data source', () => {
expect(field.values.length).toBe(6);
});
it('should fail on invalid json file upload', async () => {
const ds = new TempoDatasource(defaultSettings);
ds.uploadedJson = JSON.stringify(mockInvalidJson);
const response = await lastValueFrom(
ds.query({
targets: [{ queryType: 'upload', refId: 'A' }],
} as any)
);
expect(response.error?.message).toBeDefined();
expect(response.data.length).toBe(0);
});
it('should build search query correctly', () => {
const ds = new TempoDatasource(defaultSettings);
const tempoQuery: TempoQuery = {
@ -229,3 +241,36 @@ const secondsPromMetric = new MutableDataFrame({
{ name: 'Value #tempo_service_graph_request_server_seconds_sum', values: [10, 40] },
],
});
const mockInvalidJson = {
batches: [
{
resource: {
attributes: [],
},
instrumentation_library_spans: [
{
instrumentation_library: {},
spans: [
{
trace_id: 'AAAAAAAAAABguiq7RPE+rg==',
span_id: 'cmteMBAvwNA=',
parentSpanId: 'OY8PIaPbma4=',
name: 'HTTP GET - root',
kind: 'SPAN_KIND_SERVER',
startTimeUnixNano: '1627471657255809000',
endTimeUnixNano: '1627471657256268000',
attributes: [
{ key: 'http.status_code', value: { intValue: '200' } },
{ key: 'http.method', value: { stringValue: 'GET' } },
{ key: 'http.url', value: { stringValue: '/' } },
{ key: 'component', value: { stringValue: 'net/http' } },
],
status: {},
},
],
},
],
},
],
};

View File

@ -115,7 +115,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
if (this.uploadedJson) {
const otelTraceData = JSON.parse(this.uploadedJson as string);
if (!otelTraceData.batches) {
subQueries.push(of({ error: { message: 'JSON is not valid opentelemetry format' }, data: [] }));
subQueries.push(of({ error: { message: 'JSON is not valid OpenTelemetry format' }, data: [] }));
} else {
subQueries.push(of(transformFromOTEL(otelTraceData.batches)));
}

View File

@ -250,25 +250,28 @@ export function transformFromOTLP(
preferredVisualisationType: 'trace',
},
});
for (const data of traceData) {
const { serviceName, serviceTags } = resourceToProcess(data.resource);
for (const librarySpan of data.instrumentationLibrarySpans) {
for (const span of librarySpan.spans) {
frame.add({
traceID: transformBase64IDToHexString(span.traceId),
spanID: transformBase64IDToHexString(span.spanId),
parentSpanID: transformBase64IDToHexString(span.parentSpanId || ''),
operationName: span.name || '',
serviceName,
serviceTags,
startTime: span.startTimeUnixNano! / 1000000,
duration: (span.endTimeUnixNano! - span.startTimeUnixNano!) / 1000000,
tags: getSpanTags(span, librarySpan.instrumentationLibrary),
logs: getLogs(span),
} as TraceSpanRow);
try {
for (const data of traceData) {
const { serviceName, serviceTags } = resourceToProcess(data.resource);
for (const librarySpan of data.instrumentationLibrarySpans) {
for (const span of librarySpan.spans) {
frame.add({
traceID: transformBase64IDToHexString(span.traceId),
spanID: transformBase64IDToHexString(span.spanId),
parentSpanID: transformBase64IDToHexString(span.parentSpanId || ''),
operationName: span.name || '',
serviceName,
serviceTags,
startTime: span.startTimeUnixNano! / 1000000,
duration: (span.endTimeUnixNano! - span.startTimeUnixNano!) / 1000000,
tags: getSpanTags(span, librarySpan.instrumentationLibrary),
logs: getLogs(span),
} as TraceSpanRow);
}
}
}
} catch (error) {
return { error: { message: 'JSON is not valid OpenTelemetry format' }, data: [] };
}
return { data: [frame, ...createGraphFrames(frame)] };

View File

@ -42,6 +42,18 @@ describe('ZipkinDatasource', () => {
expect(field.type).toBe(FieldType.string);
expect(field.values.length).toBe(3);
});
it('should fail on invalid json file upload', async () => {
const ds = new ZipkinDatasource(defaultSettings);
ds.uploadedJson = JSON.stringify({ key: 'value', arr: [] });
const response = await lastValueFrom(
ds.query({
targets: [{ queryType: 'upload', refId: 'A' }],
} as any)
);
expect(response.error?.message).toBeDefined();
expect(response.data.length).toBe(0);
});
});
describe('metadataRequest', () => {

View File

@ -28,8 +28,13 @@ export class ZipkinDatasource extends DataSourceApi<ZipkinQuery> {
if (!this.uploadedJson) {
return of({ data: [] });
}
const traceData = JSON.parse(this.uploadedJson as string);
return of(responseToDataQueryResponse({ data: traceData }));
try {
const traceData = JSON.parse(this.uploadedJson as string);
return of(responseToDataQueryResponse({ data: traceData }));
} catch (error) {
return of({ error: { message: 'JSON is not valid Zipkin format' }, data: [] });
}
}
if (target.query) {