Spec-Zone.ru › OpenTofu 1.11

Рефакторинг

Ресурсы модуля​

В общих модулях и долгосрочных конфигурациях со временем первоначальная структура модулей и имена ресурсов могут перестать соответствовать вашим потребностям. Например, вы можете решить, что прежний дочерний модуль лучше разделить на два отдельных модуля, и переместить часть существующих ресурсов в новый модуль.

OpenTofu сравнивает предыдущее состояние с новой конфигурацией, сопоставляя уникальные адреса каждого модуля или ресурса. Поэтому по умолчанию OpenTofu считает перемещение или переименование объекта намерением уничтожить объект по старому адресу и создать новый объект по новому адресу.

Добавив в конфигурацию блоки moved, в которых указано, куда вы ранее переместили или переименовали объект, вы сообщаете OpenTofu, что существующий объект по старому адресу теперь относится к новому адресу.

Синтаксис блока moved​

Блок moved не принимает меток и содержит только аргументы from и to:

Блок кода
moved {
  from = aws_instance.a
  to   = aws_instance.b
}

В примере выше указано, что ресурс, известный сейчас как aws_instance.b, в предыдущей версии этого модуля назывался aws_instance.a.

Перед созданием нового плана для aws_instance.b OpenTofu сначала проверяет, есть ли в состоянии существующий объект для aws_instance.a. Если объект существует, OpenTofu переименовывает его в aws_instance.b, а затем создает план. Результирующий план будет таким, как если бы объект изначально был создан по адресу aws_instance.b, поэтому при применении конфигурации его не потребуется уничтожать.

В адресах from и to используется специальный синтаксис, позволяющий выбирать модули, ресурсы и ресурсы во вложенных модулях. Ниже описано несколько сценариев рефакторинга и подходящий синтаксис адресации для каждого из них.

  • Переименование ресурса
  • Изменение типа ресурса
  • Включение count или for_each для ресурса
  • Переименование вызова модуля
  • Включение count или for_each для вызова модуля
  • Разделение одного модуля на несколько
  • Удаление блоков moved
Примечание

Блок moved нельзя использовать с блоками ephemeral, поскольку они не сохраняются в состоянии.

Переименование ресурса​

Рассмотрим пример модуля с конфигурацией ресурса:

Блок кода
resource "aws_instance" "a" {
  count = 2

  # (resource-type-specific configuration)
}

При первом применении этой конфигурации OpenTofu создаст aws_instance.a[0] и aws_instance.a[1].

Если позднее вы решите переименовать этот ресурс, измените метку имени в блоке resource и укажите старое имя в блоке moved:

Блок кода
resource "aws_instance" "b" {
  count = 2

  # (resource-type-specific configuration)
}

moved {
  from = aws_instance.a
  to   = aws_instance.b
}

При создании следующего плана для каждой конфигурации, использующей этот модуль, OpenTofu будет считать все существующие объекты, относящиеся к aws_instance.a, созданными для aws_instance.b: aws_instance.a[0] будет считаться aws_instance.b[0], а aws_instance.a[1] — aws_instance.b[1].

Новые экземпляры модуля, в которых никогда не было aws_instance.a, проигнорируют блок moved и предложат создать aws_instance.b[0] и aws_instance.b[1] обычным образом.

В этом примере оба адреса относятся к ресурсу в целом, поэтому OpenTofu распознает перемещение для всех экземпляров ресурса. То есть оно охватывает как aws_instance.a[0], так и aws_instance.a[1] без необходимости указывать каждый из них отдельно.

Изменение типа ресурса​

У каждого типа ресурса своя схема, поэтому объекты разных типов обычно несовместимы. Однако некоторые провайдеры позволяют изменить тип объекта с одного типа ресурса на другой. Также можно заменить ресурс одного провайдера ресурсом другого провайдера, если новый провайдер поддерживает миграцию со старого ресурса. Дополнительные сведения о совместимости ресурсов см. в документации провайдера.

Вы можете использовать moved, чтобы изменить имя (а иногда и тип) ресурса, но нельзя использовать moved, чтобы преобразовать управляемый ресурс (блок resource) в ресурс данных (блок data).

Включение count или for_each для ресурса​

Рассмотрим пример модуля с ресурсом в единственном экземпляре:

Блок кода
resource "aws_instance" "a" {
  # (resource-type-specific configuration)
}

При применении этой конфигурации OpenTofu создаст объект с адресом aws_instance.a.

Позже вы используете for_each с этим ресурсом, чтобы объявить несколько экземпляров. Чтобы сохранить объект, ранее связанный только с aws_instance.a, необходимо добавить блок moved и указать ключ экземпляра, который будет присвоен объекту в новой конфигурации:

Блок кода
locals {
  instances = tomap({
    big = {
      instance_type = "m3.large"
    }
    small = {
      instance_type = "t2.medium"
    }
  })
}

resource "aws_instance" "a" {
  for_each = local.instances

  instance_type = each.value.instance_type
  # (other resource-type-specific configuration)
}

moved {
  from = aws_instance.a
  to   = aws_instance.a["small"]
}

Это не позволит OpenTofu запланировать уничтожение существующего объекта по адресу aws_instance.a. Вместо этого он будет считаться объектом, изначально созданным как aws_instance.a["small"].

Если хотя бы один из двух адресов содержит ключ экземпляра, например ["small"] в примере выше, OpenTofu считает, что оба адреса относятся к определенным экземплярам ресурса, а не к ресурсу в целом. Это означает, что moved можно использовать для переключения между ключами, а также для добавления и удаления ключей при переключении между count, for_each или ни одним из них.

Ниже приведены другие примеры допустимых блоков moved, описывающих аналогичным образом изменение ключей экземпляров ресурса:

Блок кода
# Both old and new configuration used "for_each", but the
# "small" element was renamed to "tiny".
moved {
  from = aws_instance.b["small"]
  to   = aws_instance.b["tiny"]
}

# The old configuration used "count" and the new configuration
# uses "for_each", with the following mappings from
# index to key:
moved {
  from = aws_instance.c[0]
  to   = aws_instance.c["small"]
}
moved {
  from = aws_instance.c[1]
  to   = aws_instance.c["tiny"]
}

# The old configuration used "count", and the new configuration
# uses neither "count" nor "for_each", and you want to keep
# only the object at index 2.
moved {
  from = aws_instance.d[2]
  to   = aws_instance.d
}
Примечание

Если добавить count к существующему ресурсу, в котором он не использовался, OpenTofu автоматически предложит переместить исходный объект в экземпляр с индексом ноль, если только вы явно не укажете этот ресурс в блоке moved. Тем не менее мы рекомендуем явно записывать соответствующий блок moved, чтобы будущим читателям модуля было понятнее, что изменилось.

Переименование вызова модуля​

Вызов модуля можно переименовать так же, как ресурс. Рассмотрим исходную версию модуля:

Блок кода
module "a" {
  source = "../modules/example"

  # (module arguments)
}

При применении этой конфигурации OpenTofu добавит к адресам всех ресурсов, объявленных в этом модуле, путь модуля module.a. Например, полный адрес ресурса aws_instance.example будет выглядеть так: module.a.aws_instance.example.

Если позднее вы выберете более подходящее имя для этого вызова модуля, измените метку имени в блоке module и укажите старое имя в блоке moved:

Блок кода
module "b" {
  source = "../modules/example"

  # (module arguments)
}

moved {
  from = module.a
  to   = module.b
}

При создании следующего плана для каждой конфигурации, использующей этот модуль, OpenTofu будет считать все существующие адреса объектов, начинающиеся с module.a, адресами объектов, созданных в module.b. module.a.aws_instance.example будет считаться module.b.aws_instance.example.

В этом примере оба адреса относятся к вызову модуля в целом, поэтому OpenTofu распознает перемещение для всех экземпляров вызова. Если в этом вызове модуля используются count или for_each, перемещение будет применяться ко всем экземплярам без необходимости указывать каждый из них отдельно.

Включение count или for_each для вызова модуля​

Рассмотрим пример модуля с единственным экземпляром:

Блок кода
module "a" {
  source = "../modules/example"

  # (module arguments)
}

При применении этой конфигурации OpenTofu создаст объекты, адреса которых начинаются с module.a.

В более поздних версиях модуля может потребоваться использовать count с этим ресурсом, чтобы объявить несколько экземпляров. Чтобы сохранить объект, ранее связанный только с aws_instance.a, можно добавить блок moved и указать ключ экземпляра, который будет присвоен объекту в новой конфигурации:

Блок кода
module "a" {
  source = "../modules/example"
  count  = 3

  # (module arguments)
}

moved {
  from = module.a
  to   = module.a[2]
}

Конфигурация выше указывает OpenTofu считать все объекты в module.a объектами, изначально созданными в module.a[2]. В результате OpenTofu запланирует создание новых объектов только для module.a[0] и module.a[1].

Если хотя бы один из двух адресов содержит ключ экземпляра, например [2] в примере выше, OpenTofu будет считать, что оба адреса относятся к определенным экземплярам вызова модуля, а не к вызову в целом. Это означает, что moved можно использовать для переключения между ключами, а также для добавления и удаления ключей при переключении между count, for_each или ни одним из них.

Дополнительные примеры перемещений, связанных с экземплярами, см. в похожем разделе Включение count и for_each для ресурса.

Разделение одного модуля на несколько​

По мере того как модуль расширяется для поддержки новых требований, он может вырасти настолько, что его потребуется разделить на два отдельных модуля.

Рассмотрим этот пример модуля:

Блок кода
resource "aws_instance" "a" {
  # (other resource-type-specific configuration)
}

resource "aws_instance" "b" {
  # (other resource-type-specific configuration)
}

resource "aws_instance" "c" {
  # (other resource-type-specific configuration)
}

Его можно разделить на два модуля следующим образом:

  • aws_instance.a теперь относится к модулю "x".
  • aws_instance.b также относится к модулю "x".
  • aws_instance.c относится к модулю "y".

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

  1. Создать модуль "x", скопировав в него два ресурса, которые должны в нем находиться.
  2. Создать модуль "y", скопировав в него единственный ресурс, который должен в нем находиться.
  3. Изменить исходный модуль, удалив из него все эти ресурсы и оставив только конфигурацию-переходник для миграции существующих пользователей.

Новые модули "x" и "y" должны содержать только блоки resource:

Блок кода
# module "x"

resource "aws_instance" "a" {
  # (other resource-type-specific configuration)
}

resource "aws_instance" "b" {
  # (other resource-type-specific configuration)
}
Блок кода
# module "y"

resource "aws_instance" "c" {
  # (other resource-type-specific configuration)
}

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

Блок кода
module "x" {
  source = "../modules/x"

  # ...
}

module "y" {
  source = "../modules/y"

  # ...
}

moved {
  from = aws_instance.a
  to   = module.x.aws_instance.a
}

moved {
  from = aws_instance.b
  to   = module.x.aws_instance.b
}

moved {
  from = aws_instance.c
  to   = module.y.aws_instance.c
}

Когда существующий пользователь исходного модуля обновится до новой версии «переходника», OpenTofu обнаружит эти три блока moved и будет считать, что объекты, связанные с тремя старыми адресами ресурсов, изначально были созданы внутри двух новых модулей.

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

Сценарий рефакторинга с несколькими модулями необычен тем, что нарушает обычное правило, согласно которому родительский модуль рассматривает дочерний модуль как «черный ящик» и не знает, какие именно ресурсы в нем объявлены. Этот компромисс предполагает, что все три модуля поддерживаются одной командой и распространяются вместе в одном пакете модулей.

OpenTofu разрешает ссылки на модули в блоках moved относительно экземпляра модуля, в котором они определены. Например, если бы исходный модуль выше уже был дочерним модулем с именем module.original, ссылка на module.x.aws_instance.a разрешалась бы как module.original.module.x.aws_instance.a. Модуль может задавать инструкции moved только для собственных объектов и объектов своих дочерних модулей.

Если необходимо сослаться на ресурсы внутри модуля, вызванного с помощью метааргументов count или for_each, укажите конкретный ключ экземпляра, чтобы сопоставить его с новым расположением конфигурации ресурса:

Блок кода
moved {
  from = aws_instance.example
  to   = module.new[2].aws_instance.example
}

Удаление блоков moved​

Со временем в долгоживущем модуле может накопиться много блоков moved.

Удаление блока moved обычно является несовместимым изменением: для конфигураций, в которых указан старый адрес, будет запланировано удаление существующего объекта вместо его перемещения. Мы настоятельно рекомендуем сохранять все исторические блоки moved из предыдущих версий ваших модулей, чтобы пользователи любой версии могли обновиться.

Если вы всё же решили удалить блоки moved, действуйте осторожно. Удалять блоки moved безопасно, если вы поддерживаете закрытые модули внутри организации и уверены, что все пользователи успешно выполнили tofu apply с новой версией модуля.

Если один и тот же объект необходимо переименовать или переместить дважды, рекомендуем задокументировать всю историю с помощью цепочки блоков moved, в которой новый блок ссылается на существующий:

Блок кода
moved {
  from = aws_instance.a
  to   = aws_instance.b
}

moved {
  from = aws_instance.b
  to   = aws_instance.c
}

Запись последовательности перемещений таким способом позволяет успешно обновлять конфигурации как с объектами по адресу aws_instance.a, так и конфигурации с объектами по адресу aws_instance.b. В обоих случаях OpenTofu будет считать существующий объект объектом, изначально созданным как aws_instance.c.

Устаревшие переменные и выходные значения модуля​

Рефакторинг переменных и выходных значений модуля требует дополнительных изменений со стороны вызывающих модуль конфигураций. Чтобы правильно сообщить об устаревании переменных и выходных значений, авторы модулей могут настроить конфигурацию так, чтобы она предупреждала вызывающий код о предстоящем удалении.

OpenTofu поддерживает атрибут deprecated в блоках переменных и выходных значений, в котором автор модуля может предложить способ корректной миграции при устаревании. Если конфигурация вызывающего модуля затронута, ее пользователь получит предупреждение.

Примечание

Из-за особенностей внутренней логики вычисления значений предупреждения об устаревании выходных значений модуля не отображаются для таких команд, как tofu validate. OpenTofu выводит все ожидаемые предупреждения об устаревании при выполнении команд tofu plan и tofu apply.

Дополнительные сведения см. в описании атрибута deprecated для переменных и выходных значений модулей.

Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.11/language/modules/develop/refactoring/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API