十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Terraform 插件协议中 DynamicValue 的 MessagePack 与 JSON 序列化规则是什么

Terraform 插件协议中 DynamicValue 的 MessagePack 与 JSON 序列化规则是什么 Terraform 插件协议中 DynamicValue 的 MessagePack 与 JSON 序列化规则是什么【免费下载链接】terraformTerraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.项目地址: https://gitcode.com/GitHub_Trending/te/terraform如果你在直接针对 Terraform 插件协议开发 SDK 或插件实现而不是调用已经封装好协议的 SDK就会反复遇到DynamicValue这个消息Terraform Core 在请求里用它传入resource、data、provider块求值后的数据插件在响应里也要用它返回新状态和计划值。它的结构由提供方 schema 在运行时决定所以协议把它编码成一串不透明的字节并允许 MessagePack 和 JSON 两种序列化格式。这篇文章基于仓库内 docs/plugin-protocol/object-wire-format.md 与 tfplugin5.proto、tfplugin6.proto 的定义说明这两种格式各自的选择时机、逐类型的映射规则和实现时必须满足的硬约束供你在解码请求、编码响应时逐条对照。适用前提这套线协议自 Terraform v0.12.0 起构建在 gRPC 之上DynamicValue从协议 major version 5 开始存在tfplugin5.proto当前为协议 5.10和 tfplugin6.proto当前为协议 6.11是两个 major 版本的权威定义。docs/plugin-protocol/README.md 明确指出只有随 Terraform 发布 tag 发布的.proto文件才是正式协议版本main等开发分支上的定义可能尚未定稿。插件开发者应从最近的发布 tag 取 proto 文件。同一 README 也说明大多数 provider 不直接写在这个协议上而是优先使用实现了该协议的 SDK这份文档面向的是Terraform SDK 的开发者而不是普通插件实现者。DynamicValue 消息本体两个编码字段两个 major 版本的定义完全一致以 tfplugin5.proto 为例// DynamicValue is an opaque encoding of terraform data, with the field name // indicating the encoding scheme used. message DynamicValue { bytes msgpack 1; bytes json 2; }注释点明了字段语义字段名指示所采用的编码方案。由于值的结构在运行时才由 schema 决定wire 上只能给一个字节容器由具体字段承载序列化结果。解码时如何选编码Terraform 最常用 MessagePack因为它提供更紧凑的二进制表示。但文档给出的规则是当msgpack字段没有填充时服务端实现必须回退到 JSON。也就是说解码侧要同时支持两种格式不能只实现 MessagePack。编码时如何选编码服务端必须能在各种响应消息中生产DynamicValue。文档对此的要求很明确编码时始终使用 MessagePack因为 Terraform 并没有在所有请求类型和所有版本上都一致地支持 JSON 响应。只写 JSON 响应的实现会在部分请求类型上失效。序列化由 Schema 消息驱动两种序列化都由 provider 之前返回的Schema消息驱动Terraform 按 schema 中每个值的类型约束type constraint编码选用与 Terraform 语言类型最接近的 MessagePack 或 JSON 类型。Schema.Attribute的type字段是一个 Terraform 类型约束的紧凑 JSON 序列化——要么是单个字符串原始类型要么是两个元素的数组类型种类加类型参数。由此可以推出一条实现上最实用的结论服务端可以用标准 MessagePack 或 JSON 库直接解码并假定结果符合下文描述的序列化规则。MessagePack 序列化规则类型约定文档引用的是 MessagePack 类型系统规范。MessagePack 对每个类型定义了多种可能的序列化格式Terraform 可以选择其中任意一种具体选择甚至可能在不同 Terraform 版本间变化但类型本身是契约性的。生产侧同理实现可以自由选择该类型下任何合法格式文档建议选用能无精度损失地表示该值的最紧凑格式。Block 与 Attribute 的映射一个 block 的内容被编码为 MessagePack map每个属性一个键值对每种嵌套块一个键值对。属性的编码取决于type字段映射规则如下表格内容按 object-wire-format.md 原文整理type模式MessagePack 表示stringMessagePack string内容为字符串值的 Unicode 字符、以规范化 UTF-8 序列化numberMessagePack integer、float或表示该数字的 string。以字符串表示时字符串内含十进制表示有效数字mantissa可能超出 64 位浮点的表达能力bool对应的 MessagePack boolean[list,T]与列表等长的 MessagePack array元素按同一规则以嵌套类型T编码[set,T]表示与[list,T]相同但元素顺序未定义Terraform 的 set 无序[map,T]MessagePack map元素键序列化为 map key始终是 string值按T递归编码[object,ATTRS]MessagePack mapATTRS中每个属性一个键值对属性名作为 key[tuple,TYPES]MessagePack arrayTYPES的每个元素对应一个数组元素dynamic恰好两个元素的 MessagePack array第一个是 MessagePack binary内含该值运行时类型约束的 JSON 序列化格式同本表第二个是按该类型规则编码的值本身在这张表之外还有两条优先于所有类型映射的特殊规则null 值编码为 MessagePack nilunknown 值apply 阶段才能确定的占位值编码为 MessagePack extension 值细节见下节。unknown 值的处理extension 与 refinementsunknown 值有两种表示都是 MessagePack extension旧式编码extension code 为0extension 的 payload 完全被忽略新版 Terraform 可产生带 refinements 的 unknown 值extension code 为12payload 是一个 MessagePack map用整数键区分不同种类的 refinement1nullness。布尔值true 表示最终值必然为 nullfalse 表示必然非 null键缺失表示可能为 null 也可能非 null。2字符串前缀。仅对 string 类型的 unknown 有效表示最终值已知以该字符串开头。3/4数字值的下界 / 上界。值是两元素 array第一个元素是表中合法的数字编码第二个是布尔值true 表示闭区间边界。仅对 number 类型有效。5/6集合长度list、set、map的下界 / 上界。值是整数表示包含该值的闭边界。文档对实现方给出的规则refinements 是可选信息忽略它们、把 unknown 当完全未知处理总是安全的但一个 provider 如果在其计划新状态来自PlanResourceChange中产出了 refined 值就必须在最终状态来自ApplyResourceChange中遵守这些 refinements。反序列化代码应忽略不认识的 refinement 键因为未来协议版本可能定义新的 refinement。编码无 refinement 的 unknown 值时必须使用 extension code 0而不能用 extension code 12 加一个空的 refinement maprefined unknown 值必须至少带一条 refinement。这条规则保证与 refinement 概念出现之前的旧实现向后兼容。解码侧应把任何extension code 都当作 unknown 值处理并且除非 extension code 是 12否则完全忽略 payload。未来版本的其它 extension code 也只会表示 unknown。嵌套块按 nesting 模式聚合每种嵌套块类型的各个 block 先按Schema.Block规则得到块值再由Schema.NestedBlock的nesting字段Schema.NestingBlock.NestingMode枚举决定如何聚合成一个属性值。除MAP外块不允许带标签MAP要求恰好一个标签block labelnesting值MessagePack 表示SINGLE该唯一块值的块值不存在该类型块时为 nilLIST所有块值的 MessagePack array保持配置中块的定义顺序SET所有块值的 MessagePack array顺序不保证MAPMessagePack map键为 block label值为块值GROUP同SINGLE但当该类型块不存在时Terraform 会合成一个块值所有声明的属性视为 null各声明块类型的块数为零对LIST和SET模式Terraform 保证数组元素数落在 schema 的min_items与max_items之间——除非块值中包含嵌套的 unknown 值此时 Terraform 认为值可能不完整会把元素数校验推迟到 apply 阶段例如配置中有for_each为 unknown 的dynamic块时最终块数在 apply 前不可预测。JSON 序列化规则文档把 JSON 定位为DynamicValue的次要表示MessagePack 之所以被优先是因为它能通过 extension 表示 unknown 值。文档中 JSON 规则给出的特殊覆盖规则只有一条null 值始终表示为 JSONnull。属性映射规则与 MessagePack 表结构相同区别在于各类型的落点type模式JSON 表示stringJSON stringnumberJSON number。Terraform 数字是任意精度浮点有效数字可能超出 64 位浮点的表达能力booltrue或false[list,T]与列表等长的 JSON array元素按T递归编码[set,T]与 list 表示相同元素顺序未定义[map,T]JSON object元素键作为属性名值按T递归编码[object,ATTRS]JSON objectATTRS中每个属性一个属性[tuple,TYPES]JSON arrayTYPES每个元素对应一个数组元素dynamic含两个属性的 JSON objecttype属性按表内类型模式给出值的精确运行时类型value属性按该类型规则编码的值嵌套块的nesting聚合规则与 MessagePack 一一对应唯一文字差异是不存在的SINGLE块表示为 JSONnull对应 MessagePack 的 nil。JSON 一节对LIST/SET模式的元素数保证没有附加 unknown 例外直接保证落在min_items与max_items之间。另外文档明确 JSON 编码还用于UpgradeResourceState请求中RawValue消息的json字段但那种情况下数据是按创建它的 provider 版本的 schema 序列化的未必匹配当前 provider 版本的 schema。做状态升级时要把这一点算进解析逻辑。两种编码的关键差异对照上面两张表有三处差异在实现时最容易踩dynamicMessagePack 侧是两个元素的 arraybinary 类型约束 值JSON 侧是带type/value两个属性的 object形状不同不能复用同一套解析逻辑。numberMessagePack 侧允许再用 string 表示超大有效数字JSON 侧只有 JSON number 一种落点。unknown 值只有 MessagePack 定义了表示方式extension code 0 或 12这也是响应必须用 MessagePack的根源。实现完成后的对照检查点文档给出的契约与硬约束可以作为实现自测的清单解码时按字段名取编码msgpack为空时能正确回退 JSON两种格式都能解出值。解出的值用标准 MessagePack/JSON 库即可处理类型符合上文映射表类型是契约性的具体字节格式可能随 Terraform 版本变化不要把某个具体字节形态写死成断言。生产值时只写msgpack字段。编码 unknown 值时遵守 code 0 / code 12 的分界规则无 refinement 用 code 0有 refinement 的 code 12 至少带一条 refinement且不认识的 refinement 键忽略掉。PlanResourceChange中产出的 refined 值在ApplyResourceChange的最终状态里被遵守。LIST/SET元素数校验在 MessagePack 路径上要考虑嵌套 unknown 的豁免情形。下一步在 SDK 中落地这些定义docs/plugin-protocol/README.md 给出了 SDK 开发者的操作路径把需要支持的.proto文件复制到你的仓库用protoc带 gRPC 扩展为目标语言生成 RPC stubs。README 中的 Python 示例命令文档示例按其原文保留protoc --python_out. --grpc_python_out. tfplugin5.1.proto注意本仓库docs/plugin-protocol/目录中现存的两个文件是 tfplugin5.proto 与 tfplugin6.proto使用时请取你目标版本对应的文件且以发布 tag 中的版本为准。README 还建议把协议 major 版本号纳入包名例如tfplugin5以便日后并发支持多个 major 版本升级到新的 minor 版本时原地替换旧 stubs 重新生成而支持新 major 版本则应新建包并同时支持新旧两个 major让用户不必同时升级 Terraform Core 与所有 provider。【免费下载链接】terraformTerraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.项目地址: https://gitcode.com/GitHub_Trending/te/terraform创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表