Поставщик переопределения Terraform для использования с Localstack

#terraform #localstack

Вопрос:

У меня есть репозиторий Git с кодом Terraform, который развертывается в AWS. Я добавляю Localstack в этот репозиторий, чтобы я мог выполнить тестирование проверки более высокого уровня перед планом и подать заявку в свой реальный аккаунт AWS. Чтобы использовать Localstack, я должен создать нового поставщика с пользовательской конечной точкой:

 provider "aws" {
  alias  = "real"
  region = "${local.aws_region}"
}

provider "aws" {
  alias                       = "fake"
  region                      = "${local.aws_region}"
  access_key                  = "fake"
  secret_key                  = "fake"
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    dynamodb = "http://localhost:4566"
    lambda   = "http://localhost:4566"
    kinesis  = "http://localhost:4566"
  }
}
 

Как я могу использовать одного поставщика для Localstack и одного поставщика с AWS без дублирования моего кода?

Ответ №1:

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

Первое, что нужно решить, это то, как вы будете выбирать между двумя возможностями. Поскольку ваша псевдоинфраструктура localstack неизбежно будет отличаться от «реальной» инфраструктуры, я ожидаю, что вы захотите использовать для нее отдельное состояние, и поэтому это может быть разумной ситуацией для использования отдельного рабочего пространства для разработки / тестирования, и я напишу этот пример, предполагая, что конфигурация localstack должна бытьактивен всякий dev раз, когда выбрано рабочее пространство. Если это не то, что вы хотите, то, надеюсь, этого все равно должно быть достаточно, чтобы адаптироваться в соответствии с вашими потребностями.

 locals {
  use_localstack = (terraform.workspace == "dev")

  aws_settings = (
    local.use_localstack ?
    {
      region     = local.aws_region
      access_key = "fake"
      secret_key = "fake"

      skip_credentials_validation = true
      skip_metadata_api_check     = true
      skip_requesting_account_id  = true

      override_endpoint = "http://localhost:4566"
    } :
    {
      region     = local.aws_region
      access_key = null
      secret_key = null
      
      skip_credentials_validation = null
      skip_metadata_api_check     = null
      skip_requesting_account_id  = null

      override_endpoint = null
    }
  )
}

provider "aws" {
  region     = local.aws_settings.region
  access_key = local.aws_settings.access_key
  secret_key = local.aws_settings.secret_key

  skip_credentials_validation = local.aws_settings.skip_credentials_validation
  skip_metadata_api_check     = local.aws_settings.skip_metadata_api_check
  skip_requesting_account_id  = local.aws_settings.skip_requesting_account_id

  dynamic "endpoints" {
    for_each = local.aws_settings.override_endpoint[*]
    content {
      dynamodb = endpoints.value
      lambda   = endpoints.value
      kinesis  = endpoints.value
    }
  }
}
 

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

  • При настройке аргументов, которые будут переданы поставщику, настройка null всегда совпадает с пропуском этого аргумента, поскольку Terraform внутренне обрабатывает неустановленные аргументы, неявно устанавливая их в null любом случае.
  • Использование [*] со значением, отличным от списка, автоматически преобразует его в список с нулевым элементом или список с одним элементом, в зависимости от того, является ли значение null . Это позволяет нам динамически объявлять endpoints блок, только если override_endpoint атрибут не равен нулю, а затем записывать его значение во все три переопределенных аргумента конечной точки.

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

1. Спасибо, Мартин! Это отличное решение, которое работает и сохраняет мой код сухим.

2. Единственное, чего это не касается, — это вопрос серверной части. Когда я работаю в реальном мире, я использую сегмент состояния. Если я оставляю серверную часть в коде, он предполагает реального поставщика, и я получаю No valid credential sources found for AWS Provider . Без серверной части я в порядке. Есть ли какой-либо способ условно использовать s3 против локального сервера?

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

4. Однако, поскольку в этом случае вы, вероятно, будете использовать backend "s3" в обоих случаях, и будут отличаться только аргументы, вы можете рассмотреть возможность запуска terraform init с -backend-config=FILENAME возможностью указать набор переопределенных настроек. Имейте в виду, что по умолчанию Terraform подумает, что вы пытаетесь перенести серверную часть вашей конфигурации в новое местоположение, и поэтому предложит вам об этом. Вы можете использовать эту -reconfigure опцию, чтобы указать Terraform игнорировать любую существующую конфигурацию серверной части, но вам нужно не забыть вернуться к нормальной работе перед началом работы с реальной инфраструктурой

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