#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. Отдельная конфигурация для каждого контекста и общий модуль для общих частей являются более «надежным» решением, поскольку это полностью разделяет две ситуации и значительно затрудняет случайное создание беспорядка, например, использование конфигурации поставщика разработчика против серверной части, отличной от разработчика, или наоборот.