setproduct 함수
setproduct 함수
setproduct 함수는 주어진 모든 집합의 요소들에서 가능한 모든 조합을 데카르트 곱(Cartesian product)으로 계산해 주는 함수예요. 여러 집합 멤버들의 모든 조합 집합을 구할 때 유용합니다.
출처: Packer 공식 문서
본문
setproduct 함수는 주어진 모든 집합의 요소들에서 가능한 모든 조합을 데카르트 곱을 계산해 찾아줍니다.
setproduct(sets...)
이 함수는 앱당 환경당 리소스처럼 여러 집합의 멤버들의 모든 조합을 빠짐없이 구할 때 특히 유용해요.
> setproduct(["development", "staging", "production"], ["app1", "app2"])
[
[
"development",
"app1",
],
[
"development",
"app2",
],
[
"staging",
"app1",
],
[
"staging",
"app2",
],
[
"production",
"app1",
],
[
"production",
"app2",
],
]
이 함수에는 최소 두 개의 인자를 전달해야 해요.
주로 집합을 위해 정의되지만 이 함수는 리스트에서도 동작해요. 주어진 모든 인자가 리스트라면 결과는 리스트가 되어 주어진 리스트의 순서를 유지합니다. 그렇지 않으면 결과는 집합이에요. 어느 경우든 결과의 요소 타입은 주어진 각 인자에 대응하는 값들의 리스트입니다.
Examples
이 함수의 흔한 사용 예시는 위에 있어요. 손으로 직접 쓸 때는 덜 흔하지만 재사용 가능한 모듈 상황에서 생길 수 있는 다른 상황들도 있어요.
인자 중 하나라도 비어 있으면 결과도 항상 비어 있어요. 숫자에 0을 곱하면 0이 되는 것과 비슷합니다.
> setproduct(["development", "staging", "production"], [])
[]
비슷하게 모든 인자가 단 하나의 요소만 가진다면 결과도 단 하나의 요소만 가지며, 그것은 각 인자의 첫 번째 요소예요.
> setproduct(["a"], ["b"])
[
[
"a",
"b",
],
]
각 인자는 모든 요소에 대해 일관된 타입을 가져야 해요. 그렇지 않으면 Packer가 가장 일반적인 타입으로 변환하려 시도하고, 그런 변환이 불가능하면 오류를 냅니다. 예를 들어 문자열과 숫자를 섞으면 결과 요소가 모두 일관된 타입을 가지도록 숫자가 문자열로 변환돼요.
> setproduct(["staging", "production"], ["a", 2])
[
[
"staging",
"a",
],
[
"staging",
"2",
],
[
"production",
"a",
],
[
"production",
"2",
],
]
for_each를 위한 조합 찾기
resource for_each 및 dynamic block 언어 기능은 모두 반복마다 요소가 하나씩 있는 컬렉션 값을 요구해요.
때로 입력 데이터가 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 our variables are both maps
# so we'll need to 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. See 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 we must now project it into a map
# where each key is unique. We'll 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
}
위 결과는 입력 변수의 네트워크와 서브넷 요소의 조합마다 서브넷 인스턴스 하나를 만들어 냅니다.
더 알아보기 (Learn more)
contains는 주어진 리스트나 집합이 주어진 요소 값을 포함하는지 검사해요.flatten은 두 객체 타입 사이의 관계가 명시적으로 정의된 상황에서 계층적 데이터를 단일 리스트로 평평하게 만드는 데 유용해요.setintersection은 여러 집합의 교집합을 계산해 줘요.setunion은 여러 집합의 합집합을 계산해 줘요.