Terraform 迭代嵌套数据

huangapple go评论60阅读模式
英文:

Terraform iterate over nested data

问题

我正在尝试基于提供相关配置的YAML配置文件创建New Relic的[服务级别对象][1]。
我的YAML配置:

    slo:
      targets:
      - first_slo:
        name: "我的第一个SLO"
        endpoints:
          - path: /api/method1
            method: GET
          - path: /api/method2
            method: PUT
        objectives:
          availability:
            threshold: 99.9
      - first_slo:
        name: "我的第二个SLO"
        endpoints:
          - path: /api/method12
            method: GET
          - path: /api/method23
            method: PUT
        objectives:
          availability:
            threshold: 99.99

我想要迭代这个示例配置以构建对象,但是我在使用嵌套迭代构建正确的NRQL查询时遇到了困难。

我的Terraform文件:

    resource "newrelic_service_level" "availability" {
      for_each  = var.config.slo.targets
      guid = var.guid
      name = "${each.value.name} - 可用性"
      description = "成功响应的比例。"
    
      events {
        account_id = var.account_id
        valid_events {
          from = "Transaction"
          where = "transactionType='Web' AND entityGuid='${var.guid}' AND (OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS)"
        }
        bad_events {
          from = "Transaction"
          where = "transactionType= 'Web' AND entityGuid = '${var.guid}' AND numeric(response.status) >= 500 AND (OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS)"
        }
      }
    
      objective {
        target = each.value.objectives.availability.threshold
        time_window {
          rolling {
            count = 7
            unit = "DAY"
          }
        }
      }
    }

所以基本上我在尝试的是,创建一个服务级别,其NRQL查询仅过滤出在我的配置文件中相关的特定URI和方法的组合 - 即我配置文件中的URI和方法。

因此,对于第一个SLO,`OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS` 应该被翻译成类似这样的内容:
`(request.uri = '/api/method1' AND request.method = 'GET') OR (request.uri = '/api/method2' AND request.method = 'PUT')`

我目前的解决方案是手动构建查询并将其添加到每个SLO的配置中,但这样做既不可读,也难以维护。

我将非常感激任何关于如何动态构建查询的建议。

[1]: https://registry.terraform.io/providers/newrelic/newrelic/latest/docs/resources/service_level
英文:

I'm trying to create New Relic's service-level objects based on a yaml config file that provides the relevant configuration.
My yaml configuration:

slo:
  targets:
  - first_slo:
    name: "My First SLO"
    endpoints:
      - path: /api/method1
        method: GET
      - path: /api/method2
        method: PUT
    objectives:
      availability:
        threshold: 99.9
  - first_slo:
    name: "My Second SLO"
    endpoints:
      - path: /api/method12
        method: GET
      - path: /api/method23
        method: PUT
    objectives:
      availability:
        threshold: 99.99

I want to iterate over this example configuration to build the object, but I'm struggling to form the right NRQL query using a nested iteration.

My terraform file:

resource "newrelic_service_level" "availability" {
  for_each  = var.config.slo.targets
  guid = var.guid
  name = "${each.value.name} - Availability"
  description = "Proportion of requests that are served successfully."

  events {
    account_id = var.account_id
    valid_events {
      from = "Transaction"
      where = "transactionType='Web' AND entityGuid = '${var.guid}' AND (OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS)"
    }
    bad_events {
      from = "Transaction"
      where = "transactionType= 'Web' AND entityGuid = '${var.guid}' AND numeric(response.status) >= 500 AND (OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS)"
    }
  }

  objective {
    target = each.value.objectives.availability.threshold
    time_window {
      rolling {
        count = 7
        unit = "DAY"
      }
    }
  }
}

So basically what I'm trying to do here, is create a service level with an NRQL query that filters only for the specific combination of URI and method that are relevant for this specific target - the URI and methods that I have in my config file.

So for the first SLO, OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS should translate to something like this:
(request.uri = '/api/method1' AND request.method = 'GET') OR (request.uri = '/api/method2' AND request.method = 'PUT')

My current solution would be to build the query manually and add it to the configurations for each SLO, but it is not readable and hard to maintain.

I would highly appreciate any suggestions on how to build the query dynamically.

答案1

得分: 1

你可以使用Terraform构建该查询。以下是一个示例.tf文件,展示了如何实现:

locals {
  config = yamldecode(file("${path.root}/vars.yaml"))
  parsed = [for d in local.config.slo.targets : {
    name : d["name"]
    condition : join(" OR ", [for e in d["endpoints"] : "(request.uri = '${e["path"]}' AND request.method = '${e["method"]}')"])
  }]
}

output "parsed" {
  value = local.parsed
}

这需要你的yaml文件与该文件相邻,命名为vars.yaml,并生成如下输出:

$ terraform plan

Changes to Outputs:
  + parsed = [
      + {
          + condition = "(request.uri = '/api/method1' AND request.method = 'GET') OR (request.uri = '/api/method2' AND request.method = 'PUT')"
          + name      = "My First SLO"
        },
      + {
          + condition = "(request.uri = '/api/method12' AND request.method = 'GET') OR (request.uri = '/api/method23' AND request.method = 'PUT')"
          + name      = "My Second SLO"
        },
    ]

对于你的模块,你可以在需要的地方使用join(...) 部分,重复使用应该没有问题(当然,只要你进行文档化),但如果你不喜欢这个很长的行,你可以创建一个子模块来封装它。或者你可以在预处理的locals块中构建查询字符串,可能使用 merge 函数来将查询字符串添加到每个目标配置的其余部分。

英文:

You can certainly build that query with Terraform. Here's a wee .tf file that shows how you could do it:

locals {
  config = yamldecode(file("${path.root}/vars.yaml"))
  parsed = [for d in local.config.slo.targets : {
    name : d["name"]
    condition : join(" OR ", [for e in d["endpoints"] : "(request.uri = '${e["path"]}' AND request.method = '${e["method"]}')"])
  }]
}

output "parsed" {
  value = local.parsed
}

This expects your yaml file to be sitting next to it with name vars.yaml, and produces:

$ terraform plan

Changes to Outputs:
  + parsed = [
      + {
          + condition = "(request.uri = '/api/method1' AND request.method = 'GET') OR (request.uri = '/api/method2' AND request.method = 'PUT')"
          + name      = "My First SLO"
        },
      + {
          + condition = "(request.uri = '/api/method12' AND request.method = 'GET') OR (request.uri = '/api/method23' AND request.method = 'PUT')"
          + name      = "My Second SLO"
        },
    ]

For your module, you can just use the join(...) part in place of OR_CONDITION_BETWEEN_ALL_THE_METHODS_AND_URIS. Having it repeated should be fine (as long as you document it, naturally), but if you don't like the big long line, you can create a sub-module to encapsulate it. Or you could build the query string in a pre-processing locals block, possibly using the merge function to just add the query string alongside the rest of each target's config.

huangapple
  • 本文由 发表于 2023年2月18日 08:58:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75490502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定