From 716cf170fb6d3e5bf3d023727361c465b44f702c Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Thu, 3 Jul 2025 12:45:03 +0100 Subject: [PATCH] [release-12.0.3] Graphite: Fix recursion (#107568) Graphite: Fix recursion (#106983) * Fix for repeated row variables * Add unit tests * Do not recursively replace queries that self-reference --------- (cherry picked from commit 7135832fff8d3b125d6ac59f755092873cae0a6a) Co-authored-by: Andreas Christou Co-authored-by: Ivan Ortega --- .../datasource/graphite/datasource.test.ts | 42 +++++++++++++++++++ .../plugins/datasource/graphite/datasource.ts | 10 ++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/graphite/datasource.test.ts b/public/app/plugins/datasource/graphite/datasource.test.ts index f2a73ded7eb..cb01348fdcd 100644 --- a/public/app/plugins/datasource/graphite/datasource.test.ts +++ b/public/app/plugins/datasource/graphite/datasource.test.ts @@ -510,6 +510,48 @@ describe('graphiteDatasource', () => { expect(results[0]).toBe('target=' + encodeURIComponent('avg(web01.cpu)')); }); + it('should not recursively replace queries that reference themselves', () => { + const originalTargetMap = { + A: 'sumSeries(carbon.test.test-host.cpuUsage, #A)', + }; + const results = ctx.ds.buildGraphiteParams( + { + targets: [{ target: 'sumSeries(carbon.test.test-host.cpuUsage, #A)' }], + }, + originalTargetMap + ); + expect(results[0]).toBe( + 'target=' + + encodeURIComponent('sumSeries(carbon.test.test-host.cpuUsage, sumSeries(carbon.test.test-host.cpuUsage, #A))') + ); + }); + + it('should not recursively replace queries that reference themselves, but will replace nested references', () => { + const originalTargetMap = { + A: 'sumSeries(carbon.test.test-host.cpuUsage, #A, #B)', + B: 'add(carbon.test.test-host.cpuUsage, 1.5)', + }; + const results = ctx.ds.buildGraphiteParams( + { + targets: [ + { + target: 'sumSeries(carbon.test.test-host.cpuUsage, #A, #B)', + }, + { + target: 'add(carbon.test.test-host.cpuUsage, 1.5)', + }, + ], + }, + originalTargetMap + ); + expect(results[0]).toBe( + 'target=' + + encodeURIComponent( + 'sumSeries(carbon.test.test-host.cpuUsage, sumSeries(carbon.test.test-host.cpuUsage, #A, #B), add(carbon.test.test-host.cpuUsage, 1.5))' + ) + ); + }); + it('should fix wrong minute interval parameters', () => { const originalTargetMap = { A: "summarize(prod.25m.count, '25m', 'sum')", diff --git a/public/app/plugins/datasource/graphite/datasource.ts b/public/app/plugins/datasource/graphite/datasource.ts index 3f924dce7b5..d5415d0eed5 100644 --- a/public/app/plugins/datasource/graphite/datasource.ts +++ b/public/app/plugins/datasource/graphite/datasource.ts @@ -1008,8 +1008,7 @@ export class GraphiteDatasource const graphiteOptions = ['from', 'until', 'rawData', 'format', 'maxDataPoints', 'cacheTimeout']; const cleanOptions = [], targets: Record = {}; - let target, targetValue, i; - const regex = /\#([A-Z])/g; + let target: GraphiteQuery, targetValue, i; const intervalFormatFixRegex = /'(\d+)m'/gi; let hasTargets = false; @@ -1034,7 +1033,14 @@ export class GraphiteDatasource targets[target.refId] = targetValue; } + const regex = /\#([A-Z])/g; + function nestedSeriesRegexReplacer(match: string, g1: string | number) { + // Handle the case where a query references itself to prevent infinite recursion + if (target.refId === g1) { + return targets[g1] || match; + } + // Recursively replace all nested series references return originalTargetMap[g1].replace(regex, nestedSeriesRegexReplacer) || match; }