英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论