Spec-Zone.ru › OpenTofu 1.11

Ограничения типов

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

Ключевые слова и конструкторы типов​

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

  • Ключевые слова типов — это символы без кавычек, представляющие статический тип.
  • Конструкторы типов — это символы без кавычек, за которыми следуют круглые скобки, содержащие аргумент, уточняющий информацию о типе. Без своего аргумента конструктор типа не представляет тип полностью; вместо этого он представляет разновидность похожих типов.

Ограничения типов похожи на другие виды выражений OpenTofu, но имеют специальный синтаксис. В языке OpenTofu они допустимы только в аргументе type входной переменной.

Примитивные типы​

Примитивный тип — это простой тип, который не состоит из каких-либо других типов. Все примитивные типы в OpenTofu представлены ключевым словом типа. Доступны следующие примитивные типы:

  • string: последовательность символов Unicode, представляющая текст, например "hello".
  • number: числовое значение. Тип number может представлять как целые числа, например 15, так и дробные значения, такие как 6.283185.
  • bool: либо true, либо false. Значения типа bool могут использоваться в условной логике.

Преобразование примитивных типов​

При необходимости язык OpenTofu автоматически преобразует значения number и bool в значения string, и наоборот, при условии, что строка содержит корректное представление числа или логического значения.

  • true преобразуется в "true" и наоборот
  • false преобразуется в "false" и наоборот
  • 15 преобразуется в "15" и наоборот

Сложные типы​

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

Существует две категории сложных типов: типы коллекций (для группировки похожих значений) и структурные типы (для группировки потенциально разнородных значений).

Типы коллекций​

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

Например, тип list(string) означает «список строк», что отличается от типа list(number) — списка чисел. Все элементы коллекции всегда должны принадлежать к одному и тому же типу.

В языке OpenTofu существует три вида типов коллекций:

  • list(...): последовательность значений, идентифицируемых последовательными целыми числами, начиная с нуля.

    Ключевое слово list является сокращением для list(any), которое принимает любой тип элементов при условии, что все элементы имеют одинаковый тип. Это сделано для совместимости со старыми конфигурациями; для нового кода мы рекомендуем использовать полную форму.

  • map(...): коллекция значений, каждое из которых идентифицируется строковой меткой.

    Ключевое слово map является сокращением для map(any), которое принимает любой тип элементов при условии, что все элементы имеют одинаковый тип. Это сделано для совместимости со старыми конфигурациями; для нового кода мы рекомендуем использовать полную форму.

    Отображения (maps) можно создавать с помощью фигурных скобок () и двоеточий (:) или знаков равенства (=): { "foo": "bar", "bar": "baz" } ИЛИ { foo = "bar", bar = "baz" }. Кавычки в ключах можно опускать, если только ключ не начинается с цифры — в этом случае кавычки обязательны. Запятые между парами ключ/значение обязательны для однострочных отображений. В многострочных отображениях достаточно переноса строки между парами ключ/значение.

    Примечание

    Хотя двоеточия являются допустимыми разделителями между ключами и значениями, tofu fmt их игнорирует. В отличие от этого, tofu fmt пытается выравнивать знаки равенства по вертикали.

  • set(...): коллекция уникальных значений, не имеющих вторичных идентификаторов или порядка.

Структурные типы​

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

В языке OpenTofu существует два вида структурных типов:

  • object(...): коллекция именованных атрибутов, каждый из которых имеет собственный тип.

    Схема для типов объектов имеет вид { <KEY> = <TYPE>, <KEY> = <TYPE>, ... } — пара фигурных скобок, содержащая разделенную запятыми последовательность пар <KEY> = <TYPE>. Значения, соответствующие типу объекта, должны содержать все указанные ключи, а значение каждого ключа должно соответствовать указанному для него типу. (Значения с дополнительными ключами все равно могут соответствовать типу объекта, однако лишние атрибуты отбрасываются при преобразовании типов.)

  • tuple(...): последовательность элементов, идентифицируемых последовательными целыми числами, начиная с нуля, где каждый элемент имеет собственный тип.

    Схема для типов кортежей имеет вид [<TYPE>, <TYPE>, ...] — пара квадратных скобок, содержащая разделенную запятыми последовательность типов. Значения, соответствующие типу кортежа, должны содержать ровно столько же элементов (не больше и не меньше), а значение на каждой позиции должно соответствовать типу, указанному для этой позиции.

Например: тип объекта object({ name=string, age=number }) будет соответствовать следующему значению:

Code Block
{
  name = "John"
  age  = 52
}

Кроме того, тип объекта object({ id=string, cidr_block=string }) будет соответствовать объекту, созданному ссылкой на ресурс aws_vpc, например aws_vpc.example_vpc; хотя ресурс имеет дополнительные атрибуты, они будут отброшены при преобразовании типов.

Наконец, тип кортежа tuple([string, number, bool]) будет соответствовать следующему значению:

Code Block
["a", 15, true]

Литералы сложных типов​

В языке OpenTofu есть литеральные выражения для создания значений кортежей и объектов, которые описаны в разделе Выражения: Литеральные выражения как литералы «список/кортеж» и литералы «отображение/объект» соответственно.

OpenTofu не предоставляет способа непосредственного представления списков, отображений или множеств. Однако благодаря автоматическому преобразованию сложных типов (описанному ниже) разница между похожими сложными типами практически никогда не имеет значения для обычного пользователя, и в большинстве разделов документации OpenTofu списки отождествляются с кортежами, а отображения — с объектами. Эти различия важны только при ограничении входных значений для модуля или ресурса.

Преобразование сложных типов​

Похожие разновидности сложных типов (список/кортеж/множество и отображение/объект) в языке OpenTofu обычно взаимозаменяемы, и большая часть документации OpenTofu сглаживает различия между ними. Это связано с двумя правилами преобразования:

  • По возможности OpenTofu преобразует значения между похожими разновидностями сложных типов, если предоставленное значение не соответствует в точности требуемому типу. «Похожие разновидности» определяются следующим образом:
    • Объекты и отображения похожи.
      • Отображение (или более крупный объект) может быть преобразовано в объект, если оно содержит как минимум те ключи, которые требуются схемой объекта. Любые дополнительные атрибуты отбрасываются при преобразовании, что означает, что преобразования отображение -> объект -> отображение могут приводить к потере данных.
    • Кортежи и списки похожи.
      • Список может быть преобразован в кортеж только в том случае, если он содержит ровно требуемое количество элементов.
    • Множества почти похожи как на кортежи, так и на списки:
      • При преобразовании списка или кортежа в множество повторяющиеся значения отбрасываются, а порядок элементов утрачивается.
      • При преобразовании set в список или кортеж элементы будут расположены в произвольном порядке. Если элементами множества были строки, они будут отсортированы в лексикографическом порядке; для множеств с другими типами элементов никакой определенный порядок не гарантируется.
  • По возможности OpenTofu преобразует значения элементов внутри сложного типа — либо рекурсивно преобразуя элементы сложных типов, либо как описано выше в разделе Преобразование примитивных типов.

Например: если аргумент модуля требует значение типа list(string), а пользователь передает кортеж ["a", 15, true], OpenTofu внутренне преобразует это значение в ["a", "15", "true"], приведя элементы к требуемому типу элементов string. Позже, если модуль использует эти элементы для установки различных аргументов ресурса, требующих строку, число и логическое значение (соответственно), OpenTofu автоматически преобразует вторую и третью строки обратно в требуемые типы, поскольку они содержат корректные представления числа и логического значения.

С другой стороны, автоматическое преобразование завершится сбоем, если предоставленное значение (включая любые значения его элементов) несовместимо с требуемым типом. Если аргумент требует тип map(string), а пользователь передает объект {name = ["Kristy", "Claudia", "Mary Anne", "Stacey"], age = 12}, OpenTofu выдаст ошибку несоответствия типов, так как кортеж нельзя преобразовать в строку.

Динамические типы: ограничение «any»​

Предупреждение

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

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

Единственная ситуация, когда уместно использовать any, — это передача переданного значения напрямую в другую систему без прямого обращения к его содержимому. Например, допустимо использовать переменную типа any, если вы используете ее только с jsonencode для передачи всего значения напрямую в ресурс, как показано в следующем примере:

Code Block
variable "settings" {
  type = any
}

resource "aws_s3_object" "example" {
  # ...

  # This is a reasonable use of "any" because this module
  # just writes any given data to S3 as JSON, without
  # inspecting it further or applying any constraints
  # to its type or value.
  content = jsonencode(var.settings)
}

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

any с типами коллекций​

Все элементы коллекции должны иметь одинаковый тип, поэтому если вы используете any в качестве заполнителя для типа элементов коллекции, то OpenTofu попытается найти один точный тип элементов для итоговой коллекции.

Например, при ограничении типа list(any) OpenTofu проанализирует переданное значение и попытается выбрать замену для any, которая сделает результат допустимым.

Если бы переданным значением было ["a", "b", "c"] — фактическим типом которого является tuple([string, string, string]), — OpenTofu анализирует это следующим образом:

  • Типы кортежей и типы списков похожи согласно предыдущему разделу, поэтому применяется правило преобразования кортежа в список.
  • Все элементы в кортеже являются строками, поэтому ограничение типа string будет допустимым для всех элементов списка.
  • Следовательно, в этом случае аргумент any заменяется на string, и окончательным конкретным типом значения становится list(string).

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

  • Если бы вместо этого переданным значением было ["a", 1, "b"], то OpenTofu все равно выбрал бы list(string) из-за правил преобразования примитивных типов, и итоговым значением стало бы ["a", "1", "b"] вследствие преобразования в строку, обусловленного этим ограничением типа.
  • Если бы вместо этого переданным значением было ["a", [], "b"], то значение не смогло бы соответствовать ограничению типа: не существует единого типа, к которому можно было бы преобразовать как строку, так и пустой кортеж. OpenTofu отклонил бы это значение с сообщением о том, что все элементы должны иметь одинаковый тип.

Хотя в приведенных выше примерах используется list(any), аналогичный принцип применяется к map(any) и set(any).

Необязательные атрибуты типа объекта​

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

Чтобы пометить атрибуты как необязательные, используйте модификатор optional в ограничении типа объекта. В следующем примере создаются необязательный атрибут b и необязательный атрибут со значением по умолчанию c.

Code Block
variable "with_optional_attribute" {
  type = object({
    a = string                # a required attribute
    b = optional(string)      # an optional attribute
    c = optional(number, 127) # an optional attribute with default value
  })
}

Модификатор optional принимает один или два аргумента.

  • Тип: (Обязательный) Первый аргумент указывает тип атрибута.
  • Значение по умолчанию: (Необязательный) Второй аргумент определяет значение по умолчанию, которое OpenTofu должен использовать, если атрибут отсутствует. Оно должно быть совместимо с типом атрибута. Если оно не указано, OpenTofu по умолчанию использует значение null соответствующего типа.

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

OpenTofu применяет значения атрибутов объектов по умолчанию сверху вниз во вложенных типах переменных. Это означает, что OpenTofu сначала применяет значение по умолчанию, указанное в модификаторе optional, а затем применяет любые вложенные значения по умолчанию для этого атрибута.

Пример: Вложенные структуры с необязательными атрибутами и значениями по умолчанию​

В следующем примере определяется переменная для бакетов хранилища, на которых размещается веб-сайт. Этот тип переменной использует несколько необязательных атрибутов, включая website, который сам является необязательным типом object с необязательными атрибутами и значениями по умолчанию.

Code Block
variable "buckets" {
  type = list(object({
    name    = string
    enabled = optional(bool, true)
    website = optional(object({
      index_document = optional(string, "index.html")
      error_document = optional(string, "error.html")
      routing_rules  = optional(string)
    }), {})
  }))
}

Следующий пример файла terraform.tfvars задает три конфигурации бакетов для var.buckets.

  • production настраивает правила маршрутизации для добавления перенаправления
  • archived использует конфигурацию по умолчанию, но отключен
  • docs переопределяет документы индекса и ошибок для использования текстовых файлов

Бакет production не указывает документы индекса и ошибок, а в бакете archived конфигурация веб-сайта опущена полностью. OpenTofu использует значения по умолчанию, указанные в ограничении типа bucket.

Code Block
buckets = [
  {
    name = "production"
    website = {
      routing_rules = <<-EOT
      [
        {
          "Condition" = { "KeyPrefixEquals": "img/" },
          "Redirect"  = { "ReplaceKeyPrefixWith": "images/" }
        }
      ]
      EOT
    }
  },
  {
    name = "archived"
    enabled = false
  },
  {
    name = "docs"
    website = {
      index_document = "index.txt"
      error_document = "error.txt"
    }
  },
]

Эта конфигурация приводит к следующим значениям переменной.

  • Для бакетов production и docs OpenTofu устанавливает enabled в значение true. OpenTofu также предоставляет значения по умолчанию для website, а затем значения, указанные в docs, переопределяют эти значения по умолчанию.
  • Для бакетов archived и docs OpenTofu присваивает routing_rules значение null. Когда OpenTofu не получает необязательные атрибуты и для них не указаны значения по умолчанию, OpenTofu заполняет эти атрибуты значением null.
  • Для бакета archived OpenTofu заполняет атрибут website значениями по умолчанию, указанными в ограничении типа buckets.
Code Block
tolist([
  {
    "enabled" = true
    "name" = "production"
    "website" = {
      "error_document" = "error.html"
      "index_document" = "index.html"
      "routing_rules" = <<-EOT
      [
        {
          "Condition" = { "KeyPrefixEquals": "img/" },
          "Redirect"  = { "ReplaceKeyPrefixWith": "images/" }
        }
      ]

      EOT
    }
  },
  {
    "enabled" = false
    "name" = "archived"
    "website" = {
      "error_document" = "error.html"
      "index_document" = "index.html"
      "routing_rules" = tostring(null)
    }
  },
  {
    "enabled" = true
    "name" = "docs"
    "website" = {
      "error_document" = "error.txt"
      "index_document" = "index.txt"
      "routing_rules" = tostring(null)
    }
  },
])

Пример: Условная установка необязательного атрибута​

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

С объявлением variable "buckets", показанным в предыдущем разделе, следующий пример условно переопределяет параметры index_document и error_document в объекте website на основе новой переменной var.legacy_filenames:

Code Block
variable "legacy_filenames" {
  type     = bool
  default  = false
  nullable = false
}

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

  buckets = [
    {
      name = "maybe_legacy"
      website = {
        error_document = var.legacy_filenames ? "ERROR.HTM" : null
        index_document = var.legacy_filenames ? "INDEX.HTM" : null
      }
    },
  ]
}

Когда для var.legacy_filenames установлено значение true, вызов переопределит имена файлов документов. Когда его значение равно false, вызов оставит эти два имени файлов неуказанными, что позволит модулю использовать заданные по умолчанию значения.

Copyright (c) The OpenTofu Authors
Copyright (c) 2014 HashiCorp, Inc.
Mozilla Public License, version 2.0
https://opentofu.org/docs/v1.11/language/expressions/type-constraints/

Spec-Zone.ru

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