写作与公文
用插件官方 schema 生成 Grafana 数据源…
当用户想以代码方式 provision/配置任意 Grafana 数据源插件(如 infinity、clickhouse、github),即使只说插件名而没提 provisioning 时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
用插件官方 schema 生成 Graf特定
解决思路
- 先问起点:是从零开始,还是从已有数据源转换?除非用户已明确说明,否则必须先问。从零开始则继续第 2 步;从运行实例中已有数据源转换则跳到「转换已有数据源」流程,完成后再回到第 6 步。
- 解析完整插件 id:provisioning 需要规范插件 id(`<org>-<name>-datasource`),而非用户口中的短名。若已是规范形式(含 `-datasource` 或 `-app`)则直接使用,如 `yesoreyeram-infinity-datasource`;若只有短名(如 `infinity`、`clickhouse`),用 `curl -s "https://grafana.com/api/plugins?filter=infinity" | jq -r '.items[] | "\(.slug)\t\(.name)"'` 搜索目录 API,多个匹配则列出候选并询问用户选哪个。
- 解析最新版本:`curl -s "https://grafana.com/api/plugins/yesoreyeram-infinity-datasource" | jq -r '.version'`。绝不要硬编码版本——CDN 路径按版本固定,过期版本会 404。
- 获取 settings schema(主要结构化来源):`https://plugins-cdn.grafana.net/<PLUGIN_ID>/<VERSION>/public/plugins/<PLUGIN_ID>/schema/dsconfig.json`,例如 `ID=yesoreyeram-infinity-datasource; VER=$(curl -s "https://grafana.com/api/plugins/$ID" | jq -r '.version'); curl -sf "https://plugins-cdn.grafana.net/$ID/$VER/public/plugins/$ID/schema/dsconfig.json"`。该文件遵循 dsconfig schema 规范,是解释字段语义的唯一权威,不要凭记忆推断(`valueType` 就包含 `string`、`number`、`boolean`、`array`、`object`、`map`、`any`);字段不是简单标量时查阅规范:散文规范 https://raw.githubusercontent.com/grafana/dsconfig/refs/heads/main/dsconfig/schema.md,元 schema https://raw.githubusercontent.com/grafana/dsconfig/refs/heads/main/dsconfig/schema.json。
- 从每个字段提取 provisioning 所需信息:`key`(provisioning 键)、`valueType`、`target`(`root` | `jsonData` | `secureJsonData`)、`validations`(对 `auth_method` 等选择器遵守 `allowedValues`)。只选与用户需求相关的字段(所选认证方式 + 连接),不要全选;每个字段的 `description` 会说明它属于哪种认证方式。
- 获取现成示例配置:拉取 `v0alpha1.json`,即 `https://plugins-cdn.grafana.net/<PLUGIN_ID>/<VERSION>/public/plugins/<PLUGIN_ID>/schema/v0alpha1.json`,例如 `ID=yesoreyeram-infinity-datasource; VER=$(curl -s "https://grafana.com/api/plugins/$ID" | jq -r '.version'); curl -sf "https://plugins-cdn.grafana.net/$ID/$VER/public/plugins/$ID/schema/v0alpha1.json"`。示例位于 `settingsExamples.examples`,按场景(如 `apiKey`、`oauth2ClientCredentials`)为键;每项含 `summary`/`description`(场景)和 `value`(可直接搬入文件的 `jsonData`/`secureJsonData` 载荷)。用 `... | jq -r '.settingsExamples.examples | keys[]'` 列出场景,用 `... | jq '.settingsExamples.examples.apiKey.value'` 取某个载荷。
- 无 schema 时的回退:若 `schema/dsconfig.json` 返回 404(较老插件),最后手段是参考 grafana-oss 技能(§ Data source provisioning)中的通用结构,并告知用户字段名是尽力而为、并非插件作者权威。注意 grafana-oss 技能在 `grafana-core` 插件中,也可从 https://github.com/grafana/skills 仓库作为独立技能获取。
- 按 `target` 映射每个字段:`root` → YAML 中数据源顶层键(`url`、`basicAuth`、`basicAuthUser`),Terraform 中顶层参数(`url`)或放入 `json_data_encoded`;`jsonData` → YAML 的 `jsonData:` 下,Terraform 的 `json_data_encoded = jsonencode({ … })` 内;`secureJsonData` → YAML 的 `secureJsonData:` 下以 `${ENV_VAR}` 形式,Terraform 的 `secure_json_data_encoded = jsonencode({ … })` 内并通过 `sensitive` 变量传入。按字段的 `valueType` 写标量(`string` 在 YAML 中加引号,`boolean`→`true`/`false`,`number` 不加引号)。绝不内联真实密钥。嵌套对象(`oauth2`、`aws`)和数组(`allowedHosts`、`scopes`)直接映射。
- 始终设置 `access`(`root` target)并默认 `proxy`——查询经 Grafana 服务器路由(安全默认);仅当用户明确要求时才用 `direct`(浏览器直连数据源)。Terraform 中该参数为 `access_mode`。
- 询问输出格式:YAML 还是 Terraform?字段相同但输出文件和语法不同,不要假设——「provision X」可能指任一;仅当用户已指明格式(如「terraform for X」)才跳过此问。YAML 文件 provisioning 是原生、零依赖路径;Terraform 需要官方 `grafana/grafana` provider(https://registry.terraform.io/providers/grafana/grafana/latest)。YAML 配置产出 `provisioning/datasources/<name>.yaml`;Terraform 产出 `<name>.tf`(`grafana_data_source` 资源)。`<name>` 只是文件基名,纯装饰性,因为两种加载器都会读取目录下所有文件;默认用插件名。
- 生成 YAML 文件 `provisioning/datasources/<name>.yaml`,结构为:`apiVersion: 1`,`datasources:` 下列一项,含 `name`(实例内唯一,即使不同类型的数据源也会冲突)、`type`(= schema 的 pluginType)、`access: proxy`(始终设置,默认 proxy)、`uid`(同样唯一且不可变,便于仪表盘引用)、`jsonData`(如 `auth_method` 取自 `validations.allowedValues`、`apiKeyKey`、`apiKeyType`、`allowedHosts`)、`secureJsonData`(如 `apiKeyValue: ${API_KEY}`,环境变量引用,绝不写字面密钥)、`editable: false`。
- 生成 Terraform 文件 `<name>.tf`:定义 `variable "api_key" { type = string; sensitive = true }`,以及 `resource "grafana_data_source" "infinity"`,含 `type`、`name`、`uid`、`access_mode = "proxy"`、`json_data_encoded = jsonencode({ … })`、`secure_json_data_encoded = jsonencode({ apiKeyValue = var.api_key })`。`grafana_data_source` 来自 `grafana/grafana` provider(https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/data_source),是参数名(`access_mode`、`json_data_encoded`、`secure_json_data_encoded`)的权威参考。该文件只是资源本身,`required_providers` + `provider "grafana"` 块和凭据由用户提供。
- 把文件返回给用户:在单个代码块中呈现完整文件供复制粘贴,并说明放置位置——YAML 放 `provisioning/datasources/<name>.yaml`(Grafana 启动或 provisioning 重载时生效);Terraform 放入其 Terraform 配置,用 `terraform apply` 应用。
- 可选:告知应用后如何确认生效,如 `curl -s https://grafana.example.com/api/datasources/uid/<uid>/health -H "Authorization: Bearer <token>"`(`{"status": "OK"}` 表示正常,`{"status": "ERROR"}` 表示 URL 不可达或认证配置错误),或在 UI 中访问 `<https://grafana.example.com>/connections/datasources/edit/<uid>` 并点击 Test。
- 转换已有数据源:通过 Grafana MCP server(https://github.com/grafana/mcp-grafana)读取运行实例中已配置数据源的配置。前置条件:Grafana MCP server 已连接且启用其 Datasources 工具集(它持有实例凭据)。若不可用则不支持此路径——绝不要让用户把 Grafana token 粘贴到聊天中,改为回退到从零开始的流程。
- 用 MCP 工具查找数据源:`list_datasources` 浏览,再用 `get_datasource`(按 `uid` 或 `name`)取完整配置。结果直接携带所有非密钥字段:`type`、`uid`、`url`、`access`、`basicAuth`、`basicAuthUser` 及完整 `jsonData` 对象,原样复制。
- 密钥永不返回:`secureJsonFields` 映射只列出哪些密钥键已设置(如 `{"apiKeyValue": true}`)而不含其值。对每个报告为 `true` 的键,在 `secureJsonData` 中生成 `${ENV_VAR}` 占位符。再对照 schema(第 4 步)确认密钥键名和 `target` 位置,然后照常继续第 6 步(映射)和第 7 步(生成)。
适用边界 · 注意事项
- 用户未明确起点时不要跳过第 1 步的询问;不要假设是从零开始还是从已有数据源转换。
- 不要硬编码插件版本,CDN 路径按版本固定,过期版本会 404。
- Grafana MCP server 未连接或未启用 Datasources 工具集时,不要支持「转换已有数据源」路径,也绝不要让用户把 Grafana token 粘贴到聊天中。
- 不要凭记忆推断字段语义,`valueType` 涵盖多种类型,字段非简单标量时应查阅 dsconfig 规范。
- 不要内联真实密钥,`secureJsonData` 一律用 `${ENV_VAR}` 占位符。
- 用户未指明格式时不要假设 YAML 或 Terraform,必须询问。