Терраформирование: Создание карт с соответствующими ключами не удается с помощью «дубликатов ключей объектов»

#terraform

Вопрос:

Я пытаюсь создать карту вторичных диапазонов для модуля GCP VPC здесь, и в моих локальных сетях определено следующее:

   secondary_ranges = {
  for name, config in var.subnet_config : config.subnet_name => [
      {
        range_name    = local.ip_range_pods
        ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.0.0/17"
      },
      {
        range_name    = local.ip_range_services
        ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.128.0/17"
      }
    ]
  }
 

subnet_config определяется следующим образом:

 subnet_config   = {
    cluster1 = {
        region           = "us-east1"
        subnet_name      = "default"
    },
    cluster2 = {
        region           = "us-west1"
        subnet_name      = "default"
    }
}
 

Это создает вторичные подсети просто отлично, если имена подсетей уникальны, но завершается ошибкой ниже, если имена подсетей (которые в конечном итоге являются ключевыми значениями) не уникальны:

 Two different items produced the key "default" in this 'for' expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.
 

Я пытаюсь выяснить, могу ли я использовать режим группировки, если значение представляет собой список, и если да, то как?

Любая помощь будет очень признательна.

Комментарии:

1. Ключи карты должны быть уникальными. Для этого вам потребуется использовать другую структуру данных. Вместо того , чтобы делать secondary_subnets map(list) , вы могли бы сделать это list(map) .

2. К сожалению, входные данные ожидают map(list) . Я думал о передаче динамического блока в модуль, но я не уверен, что смогу это сделать.

3. При более внимательном рассмотрении возникает более серьезная проблема, потому что вы полагаетесь на порядок отображения из keys(), который возвращается в лексическом порядке, а не в том порядке, в котором они определены.

4. secondary_ranges в этом модуле не позволит дублировать ключи диапазона, которые, как я полагаю, являются «именем». Вы можете использовать "${config.subnet_name}.${config.region}" его в качестве ключа, чтобы убедиться, что он уникален.

5. О, интересно. Даже если бы я хотел, я не могу создать соответствующие имена подсети с этим модулем (только что проверено). Тогда мне придется изменить то, как я это сделаю. Спасибо за помощь!

Ответ №1:

Если вы используете режим группировки в этом случае, то это будет группировка самого внешнего for выражения, которое создает карту, потому что это тот, по чьим ключам вы будете группироваться.

Мы можем начать с добавления модификатора режима группировки и посмотреть, что произойдет:

  secondary_ranges_pairs = {
   for name, config in var.subnet_config : config.subnet_name => [
     {
       range_name    = local.ip_range_pods
       ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.0.0/17"
     },
     {
       range_name    = local.ip_range_services
       ip_cidr_range = "10.${index(keys(var.subnet_config), name)}.128.0/17"
     }
   ]...
 }
 

Результатом приведенного выше выражения будет создание карты списков списков объектов, где самые глубокие списки-это каждая пара объектов в зависимости от того, как написано ваше внутреннее for выражение.

Чтобы превратить это в карту списков объектов, на которые, я думаю, вы надеетесь, вы можете использовать flatten ее на отдельном шаге:

   secondary_ranges = {
    for k, pairs in local.secondary_ranges_pairs : k => flatten(pairs)
  }
 

flatten рекурсивно просматривает структуру данных, в которой есть списки списков, и объединяет все вложенные списки вместе в один плоский список.


Слово предостережения: вы, похоже, используете лексический вид subnet_config ключей для получения нумерации сети. Это значит, что если вы добавляете новые элементы в свой var.subnet_config чьи ключи сортировки раньше, чем любые существующие (например, если вы добавите в cluster0 в то, что вы показали в своем вопросе), то вы неявно перенумеровать все последующие сетей, которая может вызвать много маслобойки воссоздания объектов, и изменение может даже не быть возможным, если эти сети содержат другие объекты.

Обычно я бы рекомендовал вместо этого четко указать, какой номер вы присвоили каждой сети, включив его в состав var.subnet_config объектов. Затем вы можете четко видеть, какие номера вы назначили, и убедиться, что любым новым сетям всегда будет присвоен более поздний номер, не нарушая никаких существующих назначений.

Существует также официальный модуль Terraform hashicorp/subnets/cidr , который предназначен для инкапсуляции вычислений нумерации подсетей. Дизайн этого модуля означает, что было бы не совсем просто адаптировать его для вашего варианта использования (поскольку вы выделяете два уровня подсети одновременно), но может быть полезно изучить, чтобы увидеть, имеют ли какие-либо компромиссы в дизайне, сделанные там, отношение к вашему модулю.