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.
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.