如何在Terraform插件框架中读取types.Dynamic类型的JSON数据
问题:Terraform插件框架中解析API返回的JSON字符串到
types.Dynamic字段失败 我正在使用Terraform插件框架(v1.8.0)开发Ansible Forms的自定义Terraform提供者。API返回的extravars字段是包含多类型值映射的JSON字符串,尝试用types.Dynamic类型读取该字段,但现有代码无法正常工作。
现有代码实现
数据源模型
type JobDataSourceModel struct { CxProfileName types.String `tfsdk:"cx_profile_name"` ... Extravars types.Dynamic `tfsdk:"extravars"` ... }
Schema()方法实现
func (d *JobDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ ... "extravars": schema.DynamicAttribute{ MarkdownDescription: "", Computed: true, }, ... }, } }
Read()方法实现
func (d *JobDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data JobDataSourceModel ... data.Extravars = types.DynamicValue(restInfo.Data.Extravars) ... }
API返回数据示例
{ "status": "success", "message": "job ran successfully", "data": { "id": 86, "form": "Demo Form Ansible No input", "target": "Demo Form Ansible No input", "status": "success", "start": "2024-05-02T15:35:16.000Z", "end": "2024-05-02T15:35:18.000Z", "user": "admin", "user_type": "local", "job_type": "ansible", "extravars": "{\"name\":\"vm1\",\"region\":\"myregion\",\"opco\":\"myopco\",\"svm_name\":\"mysvm_name\",\"exposure\":\"myexposure\",\"env\":\"myenv\",\"dataclass\":\"mydataclass\",\"share_name\":\"myshare_name\",\"accountid\":\"myaccountid\",\"size\":\"mysize\",\"protection_required\":\"myprotection_required\",\"ansibleforms_user\":{\"username\":\"admin\",\"id\":1,\"type\":\"local\",\"groups\":[\"local/admins\"],\"roles\":[\"public\",\"admin\"]},\"__playbook__\":\"dummy.yaml\",\"__inventory__\":\"\",\"__credentials__\":{\"vcenterCredentials\":\"VCENTER\"}}", "credentials": "{\"vcenterCredentials\":\"VCENTER\"}", "notifications": "{}", "approval": null, "step": null, "parent_id": null, "subjobs": null, "no_of_records": 45, "counter": 8, "output": "<span class='has-text-warning'>[WARNING]: No inventory was parsed, only implicit localhost is available</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n<span class='has-text-warning'>[WARNING]: provided hosts list is empty, only localhost is available. Note that</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n<span class='has-text-warning'>the implicit localhost does not match 'all'</span>\r\n<span class='has-text-warning'>[WARNING]: Found variable using reserved name: name</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n\n<span class='has-text-weight-bold'>PLAY [This is a hello-world example] *******************************************</span> <span class='tag is-info is-light'>2024-05-02 15:35:17</span>\r\n\r\n<span class='has-text-weight-bold'>TASK [Gathering Facts] *********************************************************</span>\r\n<span class='has-text-success'>ok: [localhost]</span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>\r\n\r\n<span class='has-text-weight-bold'>TASK [Output 'Welcome'.] *******************************************************</span>\r\n<span class='has-text-success'>ok: [localhost] => </span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>\r\n<span class='has-text-success'> msg: Hi there and welcome to ansible</span>\r\n\r\n<span class='has-text-weight-bold'>PLAY RECAP *********************************************************************</span>\r\n<span class=''>localhost : <span class='tag is-success'>ok=2</span> changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0</span>\r\n<span class='has-text-success'>ok: [Playbook finished] with status (0)</span> <span class='tag is-info is-light'>2024-05-02 15:35:18</span>" } }
问题原因与修复方法
问题出在直接将JSON字符串赋值给types.Dynamic类型:types.DynamicValue需要接收Terraform框架的tfjson.Value类型,而非原始JSON字符串。需先将JSON字符串解析为Go的通用类型,再转换为框架兼容的动态值。
修改后的Read()方法代码:
func (d *JobDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data JobDataSourceModel ... // 解析extravars JSON字符串为通用类型 var extravars interface{} err := json.Unmarshal([]byte(restInfo.Data.Extravars), &extravars) if err != nil { resp.Diagnostics.AddError( "解析Extravars JSON失败", "无法反序列化extravars字符串: "+err.Error(), ) return } // 将解析后的值转换为Terraform动态值 dynamicVal, err := types.DynamicValueFrom(ctx, schema.DynamicType{}, extravars) if err != nil { resp.Diagnostics.AddError( "创建动态值失败", "无法将解析后的extravars转换为动态值: "+err.Error(), ) return } data.Extravars = dynamicVal ... }
关键说明
- 使用
json.Unmarshal解析JSON字符串为interface{},保留原有的嵌套结构与多类型值 - 通过
types.DynamicValueFrom完成Go类型到Terraform框架动态值的转换,确保类型兼容 - 添加错误处理,将解析/转换过程中的异常反馈到Terraform诊断系统,便于排查问题
内容的提问来源于stack exchange,提问作者David SHainidze
相关产品推荐
相关产品推荐

