Spec-Zone.ru › OpenTofu 1.10

Рефакторинг

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

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

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

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

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

Блок кода
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 автоматически предложит переместить исходный объект в экземпляр с индексом 0, если только вы явно не укажете этот ресурс в блоке 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.10/language/modules/develop/refactoring/

Spec-Zone.ru

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