setproduct Функция
Функция setproduct находит все возможные комбинации элементов из всех заданных множеств, вычисляя декартово произведение.
setproduct(sets...)
Эта функция особенно полезна для поиска полного набора всех комбинаций элементов из нескольких множеств, например ресурсов для каждого приложения в каждой среде.
> setproduct(["development", "staging", "production"], ["app1", "app2"])
[
[
"development",
"app1",
],
[
"development",
"app2",
],
[
"staging",
"app1",
],
[
"staging",
"app2",
],
[
"production",
"app1",
],
[
"production",
"app2",
],
]Этой функции необходимо передать как минимум два аргумента.
Хотя эта функция предназначена в первую очередь для работы с множествами, она также может работать со списками. Если все переданные аргументы являются списками, результатом будет список, сохраняющий порядок исходных списков. В противном случае результатом будет множество. В обоих случаях типом элементов результата будет список значений, соответствующих каждому переданному аргументу по порядку.
Примеры
Выше приведен пример типичного использования этой функции. Существуют и менее распространенные ситуации, которые могут возникнуть при создании повторно используемых модулей.
Если любой из аргументов пуст, результат также всегда будет пустым — подобно тому, как умножение любого числа на ноль дает ноль:
> setproduct(["development", "staging", "production"], []) []
Аналогично, если каждый аргумент содержит только один элемент, результат также будет содержать только один элемент — первый элемент каждого аргумента:
> setproduct(["a"], ["b"])
[
[
"a",
"b",
],
]Все элементы каждого аргумента должны иметь единый тип. В противном случае OpenTofu попытается преобразовать их к наиболее общему типу или выдаст ошибку, если такое преобразование невозможно. Например, при смешивании строк и чисел числа преобразуются в строки, чтобы все элементы результата имели единый тип:
> setproduct(["staging", "production"], ["a", 2])
[
[
"staging",
"a",
],
[
"staging",
"2",
],
[
"production",
"a",
],
[
"production",
"2",
],
]Поиск комбинаций для for_each
Языковые конструкции ресурсный for_each и блок dynamic требуют значения коллекции, содержащего по одному элементу для каждого повторения.
Иногда входные данные представлены отдельными значениями, которые нельзя напрямую использовать в аргументе for_each. В таком случае setproduct может пригодиться, если требуется найти все уникальные комбинации элементов из нескольких разных коллекций.
Например, рассмотрим модуль, в котором объявлены следующие переменные:
variable "networks" {
type = map(object({
base_cidr_block = string
}))
}
variable "subnets" {
type = map(object({
number = number
}))
}Если требуется создать каждую из заданных подсетей для каждой заданной сети, для создания сетей верхнего уровня можно напрямую использовать var.networks, поскольку она уже имеет формат, в котором полученные экземпляры соответствуют элементам карты один к одному:
resource "aws_vpc" "example" {
for_each = var.networks
cidr_block = each.value.base_cidr_block
}Однако, чтобы объявить все подсети с помощью одного блока resource, сначала необходимо создать коллекцию, элементы которой представляют все комбинации сетей и подсетей, чтобы каждый элемент сам по себе представлял подсеть:
locals {
# setproduct works with sets and lists, but the variables are both maps
# so convert them first.
networks = [
for key, network in var.networks : {
key = key
cidr_block = network.cidr_block
}
]
subnets = [
for key, subnet in var.subnets : {
key = key
number = subnet.number
}
]
network_subnets = [
# in pair, element zero is a network and element one is a subnet,
# in all unique combinations.
for pair in setproduct(local.networks, local.subnets) : {
network_key = pair[0].key
subnet_key = pair[1].key
network_id = aws_vpc.example[pair[0].key].id
# The cidr_block is derived from the corresponding network. Refer to the
# cidrsubnet function for more information on how this calculation works.
cidr_block = cidrsubnet(pair[0].cidr_block, 4, pair[1].number)
}
]
}
resource "aws_subnet" "example" {
# local.network_subnets is a list, so project it into a map
# where each key is unique. Combine the network and subnet keys to
# produce a single unique key per instance.
for_each = {
for subnet in local.network_subnets : "${subnet.network_key}.${subnet.subnet_key}" => subnet
}
vpc_id = each.value.network_id
availability_zone = each.value.subnet_key
cidr_block = each.value.cidr_block
}Список network_subnets в примере выше создает по одному экземпляру подсети для каждой комбинации элементов сетей и подсетей во входных переменных. Например, для следующих входных данных:
networks = {
a = {
base_cidr_block = "10.1.0.0/16"
}
b = {
base_cidr_block = "10.2.0.0/16"
}
}
subnets = {
a = {
number = 1
}
b = {
number = 2
}
c = {
number = 3
}
}Результат network_subnets будет выглядеть примерно так:
[
{
"cidr_block" = "10.1.16.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "a"
},
{
"cidr_block" = "10.1.32.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "b"
},
{
"cidr_block" = "10.1.48.0/20"
"network_id" = "vpc-0bfb00ca6173ea5aa"
"network_key" = "a"
"subnet_key" = "c"
},
{
"cidr_block" = "10.2.16.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "a"
},
{
"cidr_block" = "10.2.32.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "b"
},
{
"cidr_block" = "10.2.48.0/20"
"network_id" = "vpc-0d193e011f6211a7d"
"network_key" = "b"
"subnet_key" = "c"
},
]Связанные функции
-
containsпроверяет, содержит ли заданный список или множество указанное значение элемента. -
flattenполезна для преобразования иерархических данных в один список, если связи между двумя типами объектов заданы явно. -
setintersectionвычисляет пересечение нескольких множеств. -
setsubtractвычисляет относительное дополнение двух множеств -
setunionвычисляет объединение нескольких множеств.
Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.12/language/functions/setproduct/