如何将HCL .tfvars文件解析为Go结构体?错误排查
解决.tfvars文件解析为Go结构体的错误问题
问题场景
需要将如下.tfvars文件解析为Go结构体,后续通过project.config_version等字段做字符串比较,其中project、tags、security_groups、obs为任意类型:
project = { config_version = "0.1.0" domain_name = "something" tenant_name = "example" project_name = "example" vpc_cidr = "0.0.0.0./22" enable_peering = true peer_tenant_name = "monitoring" peer_vpc_name = "vpc-1" service_project_name = "monitoring" service_project_stage = "test" enable_frontend_subnet = true frontend_subnet_cidr = "0.0.0.0/24" frontend_gateway_ip = "0.0.0.1" security_groups = { inbound_ips = ["0.0.0.0/0"] port_range_min = 1 port_range_max = 10000 } obs = { enable_monitoring = true } } tags = { Customer = "example" }
尝试解析的Go代码运行时收到错误:
不支持的参数;此处不期望名为"tags"的参数。你是否想要定义一个"tags"类型的块?,以及另外1条诊断信息
错误原因
- 标签类型不匹配:.tfvars中的
project = {...}和tags = {...}是HCL的键值对赋值语句,但Go结构体中给Project、Tags、SecurityGroups、ObsProject字段添加了,block标签,这会让gohcl尝试将其当作HCL块(如project { ... }格式)解析,导致匹配错误。 - 语法错误:
ObsProject字段的hcl标签未闭合引号:hcl:"obs,block缺少结尾的"。 - 类型不匹配:
EnableFrontendSubnet字段在.tfvars中是bool类型,原代码错误定义为string类型;port_range_min/port_range_max是数值类型,原代码定义为string类型。 - 字段名拼写错误:
FrontendSUbnetCidr拼写错误,与.tfvars中的frontend_subnet_cidr不匹配。
修正后的Go代码
package main import ( "log" "testing" "github.com/stretchr/testify/assert" "github.com/hashicorp/hcl2/gohcl" "github.com/hashicorp/hcl2/hclparse" "github.com/gruntwork-io/terratest/modules/terraform" ) // 若需要将obs作为any类型,可替换为map[string]interface{} type Obs struct { EnableMonitoring bool `hcl:"enable_monitoring"` } // 若需要将security_groups作为any类型,可替换为map[string]interface{} type SecurityGroups struct { InboundIPs []string `hcl:"inbound_ips"` PortRangeMin int `hcl:"port_range_min"` PortRangeMax int `hcl:"port_range_max"` } // 若需要将project作为any类型,可替换为map[string]interface{} type Project struct { ConfigVersion string `hcl:"config_version"` DomainName string `hcl:"domain_name"` TenantName string `hcl:"tenant_name"` ProjectName string `hcl:"project_name"` VpcCidr string `hcl:"vpc_cidr"` EnablePeering bool `hcl:"enable_peering"` PeerTenantName string `hcl:"peer_tenant_name"` PeerVpcName string `hcl:"peer_vpc_name"` ServiceProjectName string `hcl:"service_project_name"` ServiceProjectStage string `hcl:"service_project_stage"` EnableFrontendSubnet bool `hcl:"enable_frontend_subnet"` FrontendSubnetCidr string `hcl:"frontend_subnet_cidr"` FrontendGatewayIP string `hcl:"frontend_gateway_ip"` SecurityGroups *SecurityGroups `hcl:"security_groups"` Obs *Obs `hcl:"obs"` } // 若需要将tags作为any类型,可替换为map[string]interface{} type Tags struct { Customer string `hcl:"Customer"` } type ProjectTfvars struct { Project *Project `hcl:"project"` Tags *Tags `hcl:"tags"` } func readProjectTfvars(filename string) *ProjectTfvars { parser := hclparse.NewParser() f, parseDiags := parser.ParseHCLFile(filename) if parseDiags.HasErrors() { log.Fatal(parseDiags.Error()) return nil } var projectTfvars ProjectTfvars decodeDiags := gohcl.DecodeBody(f.Body, nil, &projectTfvars) if decodeDiags.HasErrors() { log.Fatal(decodeDiags.Error()) } return &projectTfvars }
关键修改说明
- 移除所有字段的
,block标签:因为.tfvars使用的是键值对赋值,而非HCL块语法。 - 修正语法错误:闭合
Obs字段的hcl标签引号。 - 匹配字段类型:将
EnableFrontendSubnet改为bool类型,PortRangeMin/PortRangeMax改为int类型,与.tfvars中的数据类型一致。 - 修正字段名拼写错误:将
FrontendSUbnetCidr改为FrontendSubnetCidr,匹配.tfvars中的键名。 - 若需要将project、tags等字段作为
any类型使用,可直接将对应结构体替换为map[string]interface{},例如:type ProjectTfvars struct { Project map[string]interface{} `hcl:"project"` Tags map[string]interface{} `hcl:"tags"` }
内容的提问来源于stack exchange,提问作者Vincent Kocsis
相关产品推荐
相关产品推荐

