From 7521d56cec167715983bdda4f53a675870678fa3 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 28 Oct 2021 09:47:35 +0200 Subject: [PATCH] CloudWatch: Handle nested panels when migrating CloudWatch queries (#40993) * support nested targets when migrating cloudwatch queries * cleanup * use panelUpgrades array * bump schema version number --- .../dashboard/state/DashboardMigrator.test.ts | 190 +++++++++++++++++- .../dashboard/state/DashboardMigrator.ts | 27 ++- .../dashboard/state/DashboardModel.ts | 1 - 3 files changed, 207 insertions(+), 11 deletions(-) diff --git a/public/app/features/dashboard/state/DashboardMigrator.test.ts b/public/app/features/dashboard/state/DashboardMigrator.test.ts index a4662d91b82..2d717941511 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.test.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.test.ts @@ -162,7 +162,7 @@ describe('DashboardModel', () => { }); it('dashboard schema version should be set to latest', () => { - expect(model.schemaVersion).toBe(31); + expect(model.schemaVersion).toBe(32); }); it('graph thresholds should be migrated', () => { @@ -1560,6 +1560,194 @@ describe('DashboardModel', () => { expect(panelTargets[2].refId).toBe('C'); expect(panelTargets[3].refId).toBe('D'); }); + + describe('with nested panels', () => { + let panel1Targets: any; + let panel2Targets: any; + let nestedModel: DashboardModel; + + beforeEach(() => { + nestedModel = new DashboardModel({ + annotations: { + list: [ + { + actionPrefix: '', + alarmNamePrefix: '', + alias: '', + dimensions: { + InstanceId: 'i-123', + }, + enable: true, + expression: '', + iconColor: 'red', + id: '', + matchExact: true, + metricName: 'CPUUtilization', + name: 'test', + namespace: 'AWS/EC2', + period: '', + prefixMatching: false, + region: 'us-east-2', + statistics: ['Minimum', 'Sum'], + }, + ], + }, + panels: [ + { + collapsed: false, + gridPos: { + h: 1, + w: 24, + x: 0, + y: 89, + }, + id: 96, + title: 'DynamoDB', + type: 'row', + panels: [ + { + gridPos: { + h: 8, + w: 12, + x: 0, + y: 0, + }, + id: 4, + options: { + legend: { + calcs: [], + displayMode: 'list', + placement: 'bottom', + }, + tooltipOptions: { + mode: 'single', + }, + }, + targets: [ + { + alias: '', + dimensions: { + InstanceId: 'i-123', + }, + expression: '', + id: '', + matchExact: true, + metricName: 'CPUUtilization', + namespace: 'AWS/EC2', + period: '', + refId: 'C', + region: 'default', + statistics: ['Average', 'Minimum', 'p12.21'], + }, + { + alias: '', + dimensions: { + InstanceId: 'i-123', + }, + expression: '', + hide: false, + id: '', + matchExact: true, + metricName: 'CPUUtilization', + namespace: 'AWS/EC2', + period: '', + refId: 'B', + region: 'us-east-2', + statistics: ['Sum'], + }, + ], + title: 'Panel Title', + type: 'timeseries', + }, + { + gridPos: { + h: 8, + w: 12, + x: 0, + y: 0, + }, + id: 4, + options: { + legend: { + calcs: [], + displayMode: 'list', + placement: 'bottom', + }, + tooltipOptions: { + mode: 'single', + }, + }, + targets: [ + { + alias: '', + dimensions: { + InstanceId: 'i-123', + }, + expression: '', + id: '', + matchExact: true, + metricName: 'CPUUtilization', + namespace: 'AWS/EC2', + period: '', + refId: 'A', + region: 'default', + statistics: ['Average'], + }, + { + alias: '', + dimensions: { + InstanceId: 'i-123', + }, + expression: '', + hide: false, + id: '', + matchExact: true, + metricName: 'CPUUtilization', + namespace: 'AWS/EC2', + period: '', + refId: 'B', + region: 'us-east-2', + statistics: ['Sum', 'Min'], + }, + ], + title: 'Panel Title', + type: 'timeseries', + }, + ], + }, + ], + }); + panel1Targets = nestedModel.panels[0].panels[0].targets; + panel2Targets = nestedModel.panels[0].panels[1].targets; + }); + + it('multiple stats query should have been split into one query per stat', () => { + expect(panel1Targets.length).toBe(4); + expect(panel2Targets.length).toBe(3); + }); + + it('new stats query should get the right statistic', () => { + expect(panel1Targets[0].statistic).toBe('Average'); + expect(panel1Targets[1].statistic).toBe('Sum'); + expect(panel1Targets[2].statistic).toBe('Minimum'); + expect(panel1Targets[3].statistic).toBe('p12.21'); + + expect(panel2Targets[0].statistic).toBe('Average'); + expect(panel2Targets[1].statistic).toBe('Sum'); + expect(panel2Targets[2].statistic).toBe('Min'); + }); + + it('new stats queries should be put in the end of the array', () => { + expect(panel1Targets[0].refId).toBe('C'); + expect(panel1Targets[1].refId).toBe('B'); + expect(panel1Targets[2].refId).toBe('A'); + expect(panel1Targets[3].refId).toBe('D'); + + expect(panel2Targets[0].refId).toBe('A'); + expect(panel2Targets[1].refId).toBe('B'); + expect(panel2Targets[2].refId).toBe('C'); + }); + }); }); }); diff --git a/public/app/features/dashboard/state/DashboardMigrator.ts b/public/app/features/dashboard/state/DashboardMigrator.ts index fa26720705d..e7eb25a474c 100644 --- a/public/app/features/dashboard/state/DashboardMigrator.ts +++ b/public/app/features/dashboard/state/DashboardMigrator.ts @@ -62,7 +62,7 @@ export class DashboardMigrator { let i, j, k, n; const oldVersion = this.dashboard.schemaVersion; const panelUpgrades: PanelSchemeUpgradeHandler[] = []; - this.dashboard.schemaVersion = 31; + this.dashboard.schemaVersion = 32; if (oldVersion === this.dashboard.schemaVersion) { return; @@ -686,6 +686,15 @@ export class DashboardMigrator { }); } + if (oldVersion < 32) { + panelUpgrades.push((panel: PanelModel) => { + this.migrateCloudWatchQueries(panel); + return panel; + }); + + this.migrateCloudWatchAnnotationQuery(); + } + if (panelUpgrades.length === 0) { return; } @@ -705,18 +714,18 @@ export class DashboardMigrator { // Migrates metric queries and/or annotation queries that use more than one statistic. // E.g query.statistics = ['Max', 'Min'] will be migrated to two queries - query1.statistic = 'Max' and query2.statistic = 'Min' // New queries, that were created during migration, are put at the end of the array. - migrateCloudWatchQueries() { - for (const panel of this.dashboard.panels) { - for (const target of panel.targets) { - if (isLegacyCloudWatchQuery(target)) { - const newQueries = migrateMultipleStatsMetricsQuery(target, [...panel.targets]); - for (const newQuery of newQueries) { - panel.targets.push(newQuery); - } + migrateCloudWatchQueries(panel: PanelModel) { + for (const target of panel.targets || []) { + if (isLegacyCloudWatchQuery(target)) { + const newQueries = migrateMultipleStatsMetricsQuery(target, [...panel.targets]); + for (const newQuery of newQueries) { + panel.targets.push(newQuery); } } } + } + migrateCloudWatchAnnotationQuery() { for (const annotation of this.dashboard.annotations.list) { if (isLegacyCloudWatchAnnotationQuery(annotation)) { const newAnnotationQueries = migrateMultipleStatsAnnotationQuery(annotation); diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 3246b275027..c9a9cbb3f24 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -1041,7 +1041,6 @@ export class DashboardModel { private updateSchema(old: any) { const migrator = new DashboardMigrator(this); migrator.updateSchema(old); - migrator.migrateCloudWatchQueries(); } resetOriginalTime() {