迁移配置设置
本指南探讨了如何将配置设置从已弃用的 AngularJS 或 Angular 升级到我们当前构建插件的 React 方法时进行转换。当我们讨论此过程中可能出现的一系列问题时,您将看到 Grafana 迁移处理工具如何使转换变得更容易。
迁移处理程序
背景
面板插件有两种类型的插件配置迁移
- AngularJS 到 React
- 插件版本更新
当您加载一个面板,而该面板的 JSON 中指定的插件版本与当前运行时版本不同时,将调用迁移处理程序。此处理程序需要返回一个有效对象,该对象不得抛出任何错误。
我们强烈建议您在升级插件可能导致现有面板出现问题时使用迁移处理程序。通过使用迁移处理程序,您将提供更好的用户体验。
使用指定部分
当您将面板插件从 AngularJS 转换为 React 时,JSON 中的编辑器自定义选项有一个特定位置。以前,使用 Angular 插件,插件可以在配置中的任何位置存储自定义对象,但现在这些对象必须使用指定的部分。
Grafana 默认情况下为其所有组件使用相同的指定部分。您添加的任何自定义组件都必须使用相同的此位置。
Grafana 在加载面板时,会在向用户显示任何内容之前调用迁移处理程序。此调用允许自动将旧的面板配置转换为正在加载的新版本。如果您没有提供迁移处理程序,那么用户将获得面板的所有默认值,并且他们必须手动修复每个面板。
深入了解 Angular 到 React 的迁移
当仪表板中的面板使用的是旧的 AngularJS 版本的插件,但实际上正在运行的是最新的 React 版本时,需要进行修改。在这种情况下,请修改旧的配置以尽可能轻松地与新插件一起使用。理想情况下,用户无需重新配置其面板。
介绍 Polystat 示例
Angular 插件通常有一个 panel.config
对象,其中包含特定于插件的设置。例如,名为 grafana-polystat-panel
的 Polystat 面板插件是一个示例插件,它最初是作为 AngularJS 面板创建的,后来移植到 React。该插件的 React 版本在 `module.ts`` 中使用 .setMigrationHandler
,如下所示
.setMigrationHandler(PolystatPanelMigrationHandler)
基于 Angular 的 Polystat 面板 (v1.x) 将大多数配置存储在“panel.polystat”对象中。您的插件应该检测该对象是否在迁移处理程序中存在,以便您可以触发转换为新的基于 React 的插件配置。
React 面板将所有内容存储在 panel.options
中。如果此对象不存在,则迁移处理程序至少应返回一个有效的空对象。如果此对象存在,则它应该只返回当前的 panel.options
。在此阶段有机会修改 React 配置,以防较新版本删除或添加了新功能。
panel.options
是一个名为 PanelModel
的接口,其类型自定义为您的面板插件。
更改 Polystat 示例中的字体
安装新版本的插件时,Grafana 服务器会调用迁移处理程序来添加或删除配置项。这些更改不会在仪表板中持久保存,因此您必须将其“保存”才能防止迁移在每次加载时都必须修改面板。
例如:在用 AngularJS 编写时,Polystat 插件有一个硬编码字体 Roboto,该字体在 Grafana 的较新版本中已删除。这会导致在 Grafana 的较新版本中运行插件时,输出呈现不正确。为了解决此问题,Grafana 添加了一个新的选择器,允许用户选择字体,但在以前版本中,全局配置没有此设置。
在这种情况下,迁移处理程序应该检测选项是否不存在,然后插入一个默认值。该默认值会根据所用 Grafana 版本返回一个有效的配置。
步骤 1:检测 Grafana 的运行时版本
您的插件可以访问变量 config.buildInfo.version
来确定正在运行的 Grafana 版本。迁移处理程序可以使用此值来设置有效默认值。
Grafana 的多个版本可能已经包含了回溯补丁,因此它们可能删除了您的插件期望的功能。在前面的示例中,它是 Roboto 字体。
迁移处理程序会获取运行时版本并使用 semver 来确定要使用哪种字体。旧版本没有将较新的 Inter 作为字体,因此加载 Roboto 最安全。较新的版本已删除 Roboto,因此插件应改为加载 Inter。
这里有两种情况
情况 1:用户使用 Roboto 运行面板
在这种情况下,用户正在运行当前的 Grafana (9.4.3) 并且有一个选择了 Roboto 的面板。该插件可以根据运行时提供不同的 Select 选项。
Polystat 面板在 module.ts
中根据运行时进行条件检查
.addSelect({
path: 'globalTextFontFamily',
name: 'Font Family',
description: 'Font used for rendered text',
category: ['Text'],
defaultValue: GLOBAL_TEXT_FONT_FAMILY,
settings: {
options: FontFamilyOptions,
},
showIf: () => hasRobotoFont() === false,
})
.addSelect({
path: 'globalTextFontFamily',
name: 'Font Family',
description: 'Font used for rendered text',
category: ['Text'],
defaultValue: GLOBAL_TEXT_FONT_FAMILY_LEGACY,
settings: {
options: FontFamilyOptionsLegacy,
},
showIf: () => hasRobotoFont() === true,
})
情况 2:Grafana 自动将字体切换到 Inter
在这种情况下,用户升级 Grafana (从 v9.3.10 升级到 v9.4.3),迁移会自动切换到使用 Inter,并且不会在字体选择器中显示 Roboto。如果升级到 9.4.0 或更高版本,则使用 Inter;否则,使用 Roboto。
Polystat 中的 MigrationHandler
包含以下代码
import { config } from "@grafana/runtime";
import { satisfies, coerce } from "semver";
export const PolystatPanelMigrationHandler = (panel: PanelModel<PolystatOptions>): Partial<PolystatOptions> => {
// set default font to inter, and check if it available, set to roboto if not
options.globalTooltipsFontFamily = FontFamilies.INTER;
if (hasRobotoFont()) {
options.globalTooltipsFontFamily = FontFamilies.ROBOTO;
}
}
export const hasRobotoFont = () => {
const version = coerce(config.buildInfo.version);
if (version !== null) {
if (satisfies(version, "<9.4.0")) {
return true;
}
}
return false;
};
步骤 2:检测缺少的配置
插件的新版本可能会添加面板中没有定义的新配置选项。可以使用迁移处理程序来添加具有“安全”默认值的新选项。
options.globalTooltipsFontFamily = FontFamilies.INTER;
if (hasRobotoFont()) {
options.globalTooltipsFontFamily = FontFamilies.ROBOTO;
}
export const PolystatPanelMigrationHandler = (panel: PanelModel<PolystatOptions>): Partial<PolystatOptions> => {
if (panel.options.NewFeature === undefined) {
// add default for new feature
panel.options.NewFeature = 5.0;
}
...
return panel.options;
}
步骤 3:检测无效的配置
由于插件正在加载并接收整个配置,因此可以遍历配置并确保值是合法的。
export const PolystatPanelMigrationHandler = (panel: PanelModel<PolystatOptions>): Partial<PolystatOptions> => {
// iterate and validate
let validConfigOptions = {
fontSize: 2,
fontFamily: 3,
defaultInvalid: 'a'
};
for (const anOption in panel.options) {
if (!validConfigOptions.includes(anOption)) {
// remove this option
console.log(`removing ${anOption}`);
delete panel.options.anOption;
}
}
return panel.options;
}
步骤 4:设置安全默认值
有时插件会删除功能或修改选项的有效选择。可以使用迁移处理程序根据需要调整配置。
export const PolystatPanelMigrationHandler = (panel: PanelModel<PolystatOptions>): Partial<PolystatOptions> => {
const featureOptions = [
'SelectionA',
'SelectionB',
'SelectionC'
];
// detect if a config setting is using a value that has been removed,
// and set a safe default
const removedOption = 'a removed option in selector';
if (!featureOptions.includes(panel.options.aSelectionSetting) {
panel.options.aSelectionSetting = featureOptions[0];
}
// new feature added, set a safe default for it
if (panel.options.aSelectionSetting === undefined) {
// add default for new feature
panel.options.aSelectionSetting = featureOptions[0];
}
return panel.options;
}