Terraform使用templatefile分离SQL模板报错,求解决方案
问题:将Terraform内嵌SQL模板移至独立文件失败
现有Terraform代码运行正常,希望把main.tf中内嵌的SQL模板迁移到独立文件,尝试使用templatefile函数但未成功,问题可能出在metric_info变量的结构或者模板重构上。
原main.tf代码
locals { # Base variables base_vars = yamldecode(file("env/${var.working_env}/vars.yaml")) # TF_VAR_working_env # Metrics subfolder names metrics = distinct([ for dir_path in fileset("../metrics-table/", "*/*/") : basename(dirname(dir_path)) ]) # Metrics configuration metrics_config = flatten([ for subfolder in local.metrics : [ for file in fileset("../metrics-table/${subfolder}", "metrics.yaml") : merge(yamldecode(file("../metrics-table/${subfolder}/${file}")), { subfolder = subfolder }) ] ]) # Metrics info metric_info = [ { metrics = merge([ for metric_config in local.metrics_config : { for metric_name, metric_data in metric_config.metrics : metric_name => { name = metric_data.name metric_table = metric_config.metric_table numerator = metric_data.numerator denominator = metric_data.denominator } } ]...) } ] } # TEMPLATE ALL_METRICS.SQL data "template_file" "all_metrics_sql" { template = <<EOT # Parte 1 do arquivo SQL -- BEGIN Parte 1 do arquivo SQL WITH %{ for metric_info in local.metric_info } %{ for metric_name, metric_data in metric_info.metrics } ${metric_name} AS ( SELECT date, '${metric_name}' AS metric_name, experiment, alternative, context_id, control, CASE WHEN version IS NULL THEN 'v1' ELSE version END AS version, device_group, platform, SUM(${metric_data.denominator}) AS denominator, SUM(${metric_data.numerator}) AS numerator, SAFE_DIVIDE(SUM(${metric_data.numerator}), SUM(${metric_data.denominator})) AS metric_value FROM `${local.base_vars.project}.ab_experiment_metrics.${metric_data.metric_table}` WHERE outlier = FALSE GROUP BY date, experiment, alternative, context_id, control, version, device_group, platform, metric_name ), %{ endfor } %{ endfor } -- END Parte 1 do arquivo SQL # Parte 2 do arquivo SQL -- BEGIN Parte 2 do arquivo SQL all_metrics AS ( SELECT * FROM conversion_rate %{ for metric_info in local.metric_info } %{ for metric_name, _ in metric_info.metrics } UNION ALL SELECT * FROM ${metric_name} %{ endfor } %{ endfor } ), all_metrics_final as ( SELECT date, metric_name, all_metrics.experiment, alternative, control, version, device_group, platform, denominator, SUM(denominator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS denominator_cumsum, numerator, SUM(numerator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS numerator_cumsum, metric_value FROM all_metrics ) SELECT * FROM all_metrics_final -- END Parte 2 do arquivo SQL EOT } # CREATE ALL_METRICS.SQL resource "local_file" "all_metrics_sql" { filename = "all_metrics.sql" content = data.template_file.all_metrics_sql.rendered } # OUTPUTS output "metrics_config" { value = local.metrics_config } output "metric_info" { value = local.metric_info } output "all_metrics_sql_content" { value = data.template_file.all_metrics_sql.rendered }
输出变更
Changes to Outputs: + metric_info = [ + { + metrics = { + time_to_first_play_per_login = { + denominator = "experiment_login_count" + metric_table = "time_to_first_play" + name = "time_to_first_play_per_login" + numerator = "total_time_to_first_play" } + time_to_first_play_per_session = { + denominator = "experiment_session_count" + metric_table = "time_to_first_play" + name = "time_to_first_play_per_session" + numerator = "total_time_to_first_play" } + time_to_first_play_per_user = { + denominator = "experiment_user_count" + metric_table = "time_to_first_play" + name = "time_to_first_play_per_user" + numerator = "total_time_to_first_play" } + time_to_second_play_per_creator = { + denominator = "experiment_user_count" + metric_table = "time_to_second_play" + name = "time_to_second_play_per_user" + numerator = "total_time_to_second_play" } + time_to_second_play_per_login = { + denominator = "experiment_user_count" + metric_table = "time_to_second_play" + name = "time_to_second_play_per_user" + numerator = "total_time_to_second_play" } + time_to_second_play_per_name = { + denominator = "experiment_session_count" + metric_table = "time_to_second_play" + name = "time_to_second_play_per_session" + numerator = "total_time_to_second_play" } } }, ] + metrics_config = [ + { + metric_table = "time_to_first_play" + metrics = { + time_to_first_play_per_login = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_login_count" + description = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Usuário" + metric_type_id = 2 + name = "time_to_first_play_per_login" + numerator = "total_time_to_first_play" } + time_to_first_play_per_session = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_session_count" + description = "Tempo médio que cada sessão leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Sessão" + metric_type_id = 2 + name = "time_to_first_play_per_session" + numerator = "total_time_to_first_play" } + time_to_first_play_per_user = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_user_count" + description = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Usuário" + metric_type_id = 2 + name = "time_to_first_play_per_user" + numerator = "total_time_to_first_play" } } + subfolder = "time_to_first_play" }, + { + metric_table = "time_to_second_play" + metrics = { + time_to_second_play_per_creator = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_user_count" + description = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Usuário" + metric_type_id = 2 + name = "time_to_second_play_per_user" + numerator = "total_time_to_second_play" } + time_to_second_play_per_login = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_user_count" + description = "Tempo médio que cada usuário leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Usuário" + metric_type_id = 2 + name = "time_to_second_play_per_user" + numerator = "total_time_to_second_play" } + time_to_second_play_per_name = { + active = 1 + category = "Métrica de vídeo" + denominator = "experiment_session_count" + description = "Tempo médio que cada sessão leva para dar o primeiro play em algum vídeo" + metric_title = "Tempo para o Primeiro Play por Sessão" + metric_type_id = 2 + name = "time_to_second_play_per_session" + numerator = "total_time_to_second_play" } } + subfolder = "time_to_second_play" }, ]
尝试的错误代码
data "template_file" "all_metrics_sql" { template = templatefile("${path.module}/template.tftpl", { metric_info = local.metric_info.metrics }) }
报错信息
╷ │ Error: Not enough function arguments │ │ on main.tf line 69, in data "template_file" "all_metrics_sql": │ 69: template = templatefile("arquivo_template.tpl") │ │ Function "templatefile" expects 2 argument(s). Missing value for │ "vars". ╵ ╷ │ Error: Unsupported attribute │ │ on main.tf line 72, in data "template_file" "all_metrics_sql": │ 72: metric_info = local.metric_info.metrics │ ├──────────────── │ │ local.metric_info is tuple with 1 element │ │ This value does not have any attributes.
解决方案
1. 修正变量引用错误
local.metric_info是包含单个元素的元组,必须通过索引[0]访问内部元素后,再获取metrics属性,即local.metric_info[0].metrics。
2. 重构模板文件与代码
步骤1:创建独立模板文件template.tftpl
将原内嵌的SQL模板内容复制到该文件,同时调整循环逻辑(直接遍历传入的metrics字典,无需嵌套外层循环):
# Parte 1 do arquivo SQL -- BEGIN Parte 1 do arquivo SQL WITH %{ for metric_name, metric_data in metrics } ${metric_name} AS ( SELECT date, '${metric_name}' AS metric_name, experiment, alternative, context_id, control, CASE WHEN version IS NULL THEN 'v1' ELSE version END AS version, device_group, platform, SUM(${metric_data.denominator}) AS denominator, SUM(${metric_data.numerator}) AS numerator, SAFE_DIVIDE(SUM(${metric_data.numerator}), SUM(${metric_data.denominator})) AS metric_value FROM `${base_vars.project}.ab_experiment_metrics.${metric_data.metric_table}` WHERE outlier = FALSE GROUP BY date, experiment, alternative, context_id, control, version, device_group, platform, metric_name ), %{ endfor } -- END Parte 1 do arquivo SQL # Parte 2 do arquivo SQL -- BEGIN Parte 2 do arquivo SQL all_metrics AS ( SELECT * FROM conversion_rate %{ for metric_name, _ in metrics } UNION ALL SELECT * FROM ${metric_name} %{ endfor } ), all_metrics_final as ( SELECT date, metric_name, all_metrics.experiment, alternative, control, version, device_group, platform, denominator, SUM(denominator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS denominator_cumsum, numerator, SUM(numerator) OVER (PARTITION BY metric_name, all_metrics.experiment, alternative, version, device_group, platform ORDER BY date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS numerator_cumsum, metric_value FROM all_metrics ) SELECT * FROM all_metrics_final -- END Parte 2 do arquivo SQL
步骤2:修改main.tf中的SQL生成逻辑
直接使用templatefile函数生成内容,无需依赖template_file数据源(Terraform推荐直接使用函数替代旧版数据源):
# 替换原template_file数据源与local_file资源 resource "local_file" "all_metrics_sql" { filename = "all_metrics.sql" content = templatefile("${path.module}/template.tftpl", { base_vars = local.base_vars metrics = local.metric_info[0].metrics }) }
内容的提问来源于stack exchange,提问作者lubi_nascimento
相关产品推荐
相关产品推荐

