mirror of
https://gitlab.sarex.io/infra/terraform-contour-mirror.git
synced 2026-08-05 18:31:00 +03:00
Merge branch 'feature/prod-adopt' into 'master'
adopt prod kafka/rmq/secrets into terraform See merge request infra/terraform!20
This commit is contained in:
commit
43b28d0c85
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ override.tf.json
|
||||
*_override.tf.json
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
.claude/
|
||||
|
||||
# Terragrunt
|
||||
.terragrunt-cache/
|
||||
|
||||
@ -46,7 +46,7 @@ trigger-downstream-prod:
|
||||
pipeline_variables: true
|
||||
yaml_variables: true
|
||||
variables:
|
||||
TERRAFORM_IMAGE_NAME: cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.10
|
||||
TERRAFORM_IMAGE_NAME: cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.11
|
||||
YC_SERVICE_ACCOUNT_KEY_FILE: ${YC_PROD_SERVICE_ACCOUNT_KEY_FILE}
|
||||
YC_CLOUD_ID: ${YC_CLOUD_ID}
|
||||
YC_STAGE_FOLDER_ID: ${YC_STAGE_FOLDER_ID}
|
||||
|
||||
4
.sops.yaml
Normal file
4
.sops.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
creation_rules:
|
||||
- path_regex: infrastructure-secrets\.yaml$
|
||||
encrypted_regex: "^(data)$"
|
||||
age: age198ea80z7gan2ruetn288ln6rl2qh7xtsqqpwrjtyn8maya3364fsvpdd63
|
||||
@ -10,10 +10,15 @@ RUN curl -fsSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/ter
|
||||
rm -f terraform.zip
|
||||
|
||||
# Terragrunt
|
||||
ARG TERRAGRUNT_VERSION=0.69.1
|
||||
ARG TERRAGRUNT_VERSION=0.93.11
|
||||
RUN curl -fsSL https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 -o /usr/local/bin/terragrunt && \
|
||||
chmod +x /usr/local/bin/terragrunt
|
||||
|
||||
# SOPS (расшифровка secret-values через age; ключ передаётся в CI через SOPS_AGE_KEY)
|
||||
ARG SOPS_VERSION=3.9.0
|
||||
RUN curl -fsSL https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64 -o /usr/local/bin/sops && \
|
||||
chmod +x /usr/local/bin/sops
|
||||
|
||||
# Plugin cache и mirror для Yandex Cloud
|
||||
RUN mkdir -p /root/.terraform.d/plugin-cache && \
|
||||
mkdir -p /root/.terraform.d && \
|
||||
|
||||
99
README.md
99
README.md
@ -7,6 +7,9 @@ Infrastructure as Code для управления ресурсами Yandex Clo
|
||||
```
|
||||
terraform/
|
||||
├── infrastructure.yaml # Конфигурация всех ресурсов (единый источник правды)
|
||||
├── infrastructure-secrets.yaml # sops-зашифрованные секретные значения
|
||||
├── .sops.yaml # правило шифрования
|
||||
├── SOPS.md # как работать с sops и infrastructure-secrets.yaml
|
||||
├── live/ # Terragrunt конфигурации по окружениям
|
||||
│ ├── terragrunt.hcl # Общие настройки (backend, providers)
|
||||
│ └── stage/ # Stage окружение
|
||||
@ -67,14 +70,8 @@ environments:
|
||||
patterns: allkeys
|
||||
pubSubChannels: allchannels
|
||||
|
||||
secrets: # Kubernetes secrets
|
||||
- name: my-secret
|
||||
namespace: pulse
|
||||
type: database # database | database_url | valkey | s3 | dockerconfigjson | opaque
|
||||
dependencies:
|
||||
cluster: "xxx"
|
||||
db: mydb
|
||||
user: myuser
|
||||
# k8s-секретов здесь НЕТ — они описываются целиком в infrastructure-secrets.yaml
|
||||
# (форма + значения, шифруется только data). См. раздел «k8s-secret» ниже.
|
||||
```
|
||||
|
||||
### Terragrunt
|
||||
@ -177,40 +174,68 @@ valkey_users:
|
||||
|
||||
### k8s-secret
|
||||
|
||||
Создание Kubernetes secrets с поддержкой зависимостей.
|
||||
Создаёт Kubernetes secrets. **Секреты описываются целиком в одном файле — зашифрованном
|
||||
`infrastructure-secrets.yaml`** (форма + значения). В `infrastructure.yaml` секретов нет.
|
||||
Ресурс в state адресуется парой `namespace/name` (поле `resource_key` удалено).
|
||||
|
||||
**Типы секретов:**
|
||||
- `database` — host, port, database, username, password, ca.crt
|
||||
- `database_url` — database_url в формате postgresql+asyncpg
|
||||
- `valkey` — cert, host, login, password, port, url
|
||||
- `s3` — access_key, secret_key, bucket, endpoint
|
||||
- `dockerconfigjson` — credentials для container registry
|
||||
- `opaque` — произвольные данные
|
||||
Каждый секрет описывается **в одном из двух режимов** (не смешивать):
|
||||
|
||||
**Особенности:**
|
||||
- Автоматическое получение данных из database/s3 модулей
|
||||
- Поддержка `random_keys` для генерации паролей
|
||||
- Поддержка `custom_keys` для статических значений
|
||||
- `ignore_changes` для предотвращения перезаписи
|
||||
|
||||
**Пример:**
|
||||
**Режим A — adopt / статический: явная `data`.**
|
||||
Значения заданы прямо (base64, как в k8s `.data`), шифруется только `data`. Модуль кладёт их
|
||||
в секрет 1:1 и ничего не вычисляет. Используется для существующих секретов, взятых под terraform,
|
||||
и для секретов со статическими значениями.
|
||||
```yaml
|
||||
secrets:
|
||||
- name: my-db-secret
|
||||
namespace: pulse
|
||||
type: database
|
||||
dependencies:
|
||||
cluster: "c9qa2coo5ukgcg93fldm"
|
||||
db: mydb
|
||||
user: myuser
|
||||
random_keys:
|
||||
session_key:
|
||||
length: 32
|
||||
special: false
|
||||
lifecycle:
|
||||
ignore_changes: true
|
||||
- name: my-secret
|
||||
namespace: myns
|
||||
type: opaque # kafka|rabbitmq|database|database_url|valkey|s3|yc_sa|dockerconfigjson|opaque
|
||||
lifecycle: { ignore_changes: false }
|
||||
data:
|
||||
username: <base64>
|
||||
password: <base64>
|
||||
```
|
||||
|
||||
**Режим B — динамический: `depends_on` + маппинг ключей.**
|
||||
Значения собираются из outputs других юнитов (kafka/rabbitmq/database/valkey/s3/yc_sa),
|
||||
которые создал terragrunt. **`data` НЕ указывается.**
|
||||
```yaml
|
||||
- name: my-kafka-secret
|
||||
namespace: myns
|
||||
type: kafka
|
||||
depends_on: { kafka_ref: prod, kafka_user: myservice } # какой облачный ресурс читать
|
||||
credential_keys: # какие поля источника и под каким ключом положить в секрет
|
||||
username: username
|
||||
password: password
|
||||
host: host
|
||||
custom_keys: { ssl: "true" } # статические несекретные литералы
|
||||
constant_keys: { ca.crt: kafka_ca } # значения из общих constants (CA и т.п.)
|
||||
random_keys: { token: { length: 32, special: false } } # сгенерировать random_password
|
||||
```
|
||||
|
||||
Что означают поля режима B:
|
||||
- **`depends_on`** — указатель на облачный ресурс-источник кредов. Для kafka —
|
||||
`kafka_ref:kafka_user`, для rabbitmq — `rabbitmq_ref:rabbitmq_vhost:rabbitmq_user`, для
|
||||
database — `cluster:db:user`, для valkey — `valkey_cluster:valkey_user`, для s3 — `bucket`,
|
||||
для yc_sa — `service_account`. Модуль берёт host/port/user/password/… из outputs этого юнита.
|
||||
- **`credential_keys`** — `{ ключ_в_секрете: имя_поля_источника }`; выбирает/переименовывает поля.
|
||||
Если не задан — берутся все дефолтные поля типа.
|
||||
- **`custom_keys`** — статические несекретные литералы прямо в секрет.
|
||||
- **`constant_keys`** — значения из общих `constants` (`kafka_ca`, `postgres_ca`, …).
|
||||
- **`random_keys`** — генерируемые `random_password` (токены/сессии).
|
||||
|
||||
**Типы (`type`)** и дефолтные поля: `database` (host/port/database/username/password/ca.crt),
|
||||
`database_url` (postgresql+asyncpg URL), `valkey` (cert/host/login/password/port/url),
|
||||
`kafka`/`rabbitmq` (host/port/user/password/…), `s3` (access_key/secret_key/bucket/endpoint),
|
||||
`yc_sa` (access_key/secret_key/service_account_id/endpoint), `dockerconfigjson`, `opaque`.
|
||||
|
||||
**Как расшифровать/отредактировать секрет:** `sops infrastructure-secrets.yaml`. После правки
|
||||
руками — `sops -e -i infrastructure-secrets.yaml`. Расшифрованный файл не коммитить.
|
||||
|
||||
**Как завести секрет после создания kafka-юзера/топиков (или rmq vhost/user):** добавить запись
|
||||
в режиме B — `type`, `depends_on` на созданный ресурс, `credential_keys` для нужных полей; `data`
|
||||
не указывать. terragrunt подставит значения из outputs соответствующего юнита.
|
||||
|
||||
Подробнее про sops: [SOPS.md](SOPS.md).
|
||||
|
||||
### k8s-namespace
|
||||
|
||||
Создание Kubernetes namespaces.
|
||||
|
||||
43
SOPS.md
Normal file
43
SOPS.md
Normal file
@ -0,0 +1,43 @@
|
||||
# sops и infrastructure-secrets.yaml
|
||||
|
||||
`infrastructure-secrets.yaml` хранит секретную часть k8s-секретов. В `infrastructure.yaml` остаётся публичное описание: имя секрета, неймспейс, тип, зависимости и набор ключей.
|
||||
|
||||
Формат файла:
|
||||
|
||||
```yaml
|
||||
environments:
|
||||
prod:
|
||||
secrets:
|
||||
- name: yc-kafka-secret
|
||||
namespace: eav-prod
|
||||
data:
|
||||
username: <base64>
|
||||
password: <base64>
|
||||
host: <base64>
|
||||
```
|
||||
|
||||
`name` и `namespace` — реальные имя и неймспейс k8s-секрета. `data` — значения в том же виде, что в k8s `.data`, то есть base64.
|
||||
|
||||
`resource_key` в этот файл обычно не пишется. Это внутренний ключ terraform для state, а не имя секрета в кластере. Если совпадение по `namespace/name` невозможно, `resource_key` можно указать явно, но это исключение.
|
||||
|
||||
Редактирование:
|
||||
|
||||
```bash
|
||||
sops infrastructure-secrets.yaml
|
||||
```
|
||||
|
||||
После ручной правки расшифрованного файла:
|
||||
|
||||
```bash
|
||||
sops -e -i infrastructure-secrets.yaml
|
||||
```
|
||||
|
||||
В ci `scripts/generate-pipeline.sh` расшифровывает файл во временный путь и передаёт его в terragrunt через `INFRA_SECRET_VALUES_FILE`. Расшифрованный файл не должен попадать в git и артефакты.
|
||||
|
||||
Для adopt существующего секрета:
|
||||
|
||||
```bash
|
||||
kubectl --context yc-sarex-prod -n <namespace> get secret <name> -o jsonpath='{.data}'
|
||||
```
|
||||
|
||||
Полученную `.data` нужно перенести в `environments.prod.secrets[]` без декодирования.
|
||||
684
infrastructure-secrets.yaml
Normal file
684
infrastructure-secrets.yaml
Normal file
File diff suppressed because one or more lines are too long
1578
infrastructure.yaml
1578
infrastructure.yaml
File diff suppressed because it is too large
Load Diff
@ -35,10 +35,13 @@ inputs = {
|
||||
vhosts = try(local.rabbitmq_config.vhosts, [])
|
||||
users = try(local.rabbitmq_config.users, [])
|
||||
permissions = try(local.rabbitmq_config.permissions, [])
|
||||
topic_permissions = try(local.rabbitmq_config.topic_permissions, [])
|
||||
exchanges = try(local.rabbitmq_config.exchanges, [])
|
||||
queues = try(local.rabbitmq_config.queues, [])
|
||||
bindings = try(local.rabbitmq_config.bindings, [])
|
||||
policies = try(local.rabbitmq_config.policies, [])
|
||||
unmanaged_output_users = try(local.rabbitmq_config.unmanaged_output_users, [])
|
||||
unmanaged_output_permissions = try(local.rabbitmq_config.unmanaged_output_permissions, [])
|
||||
|
||||
yc_token = get_env("YC_TOKEN", "")
|
||||
yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "")
|
||||
|
||||
@ -49,6 +49,15 @@ dependency "rabbitmq" {
|
||||
skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true"
|
||||
}
|
||||
|
||||
dependency "kafka_topics" {
|
||||
config_path = "../kafka-topics"
|
||||
mock_outputs = {
|
||||
kafka_outputs_map = {}
|
||||
}
|
||||
mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"]
|
||||
skip_outputs = get_env("TG_SKIP_DEPENDENCY_OUTPUTS", "false") == "true"
|
||||
}
|
||||
|
||||
dependency "valkey_users" {
|
||||
config_path = "../valkey-users"
|
||||
mock_outputs = {
|
||||
@ -121,10 +130,55 @@ LpuQKbSbIERsmR+QqQ==
|
||||
-----END CERTIFICATE-----
|
||||
EOT
|
||||
|
||||
repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..")
|
||||
infra_config = yamldecode(file("${local.repo_root}/infrastructure.yaml"))
|
||||
env_name = basename(dirname(get_terragrunt_dir()))
|
||||
env_config = local.infra_config.environments[local.env_name]
|
||||
repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..")
|
||||
env_name = basename(dirname(get_terragrunt_dir()))
|
||||
|
||||
# Единый декларативный источник: форма + данные секретов в одном sops-файле
|
||||
# infrastructure-secrets.yaml (шифруется только data). В CI файл расшифровывается
|
||||
# заранее и путь передаётся через INFRA_SECRET_VALUES_FILE; локально - fallback
|
||||
# на прямую расшифровку через sops_decrypt_file. Значения передаются в модуль
|
||||
# только через inputs (эфемерно), чтобы расшифрованные данные не оседали на диске.
|
||||
secret_values_env_file = get_env("INFRA_SECRET_VALUES_FILE", "")
|
||||
secret_values_raw = local.secret_values_env_file != "" ? file(local.secret_values_env_file) : try(sops_decrypt_file("${local.repo_root}/infrastructure-secrets.yaml"), "")
|
||||
secret_values_config = try(yamldecode(local.secret_values_raw), {})
|
||||
secret_values_env = try(local.secret_values_config.environments[local.env_name], {})
|
||||
|
||||
secrets = try(local.secret_values_env.secrets, [])
|
||||
|
||||
# Нормализованный список для модуля. Ключ ресурса теперь namespace/name,
|
||||
# resource_key больше не используется.
|
||||
secrets_input = [
|
||||
for secret in local.secrets : {
|
||||
name = secret.name
|
||||
namespace = secret.namespace
|
||||
type = try(secret.type, "opaque")
|
||||
registry_url = try(secret.registry_url, "")
|
||||
data = try(secret.data, {})
|
||||
depends_on = try(secret.depends_on, {})
|
||||
yc_sa_key_names = try(secret.yc_sa_key_names, {})
|
||||
custom_keys = try(secret.custom_keys, {})
|
||||
constant_keys = try(secret.constant_keys, {})
|
||||
random_keys = try(secret.random_keys, {})
|
||||
credential_keys = try(secret.credential_keys, {})
|
||||
labels = try(secret.labels, {})
|
||||
annotations = try(secret.annotations, {})
|
||||
ignore_changes = try(secret.lifecycle.ignore_changes, false)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
generate "import_existing" {
|
||||
path = "import_existing.tf"
|
||||
if_exists = "overwrite"
|
||||
contents = join("\n\n", [
|
||||
for secret in local.secrets : <<-EOF
|
||||
import {
|
||||
to = kubernetes_secret.without_ignore["${secret.namespace}/${secret.name}"]
|
||||
id = "${secret.namespace}/${secret.name}"
|
||||
}
|
||||
EOF
|
||||
if try(secret.adopt, false) && try(secret.lifecycle.ignore_changes, false) == false
|
||||
])
|
||||
}
|
||||
|
||||
terraform {
|
||||
@ -132,22 +186,7 @@ terraform {
|
||||
}
|
||||
|
||||
inputs = {
|
||||
secrets = [
|
||||
for secret in local.env_config.secrets : {
|
||||
name = secret.name
|
||||
namespace = secret.namespace
|
||||
secret_type = secret.type
|
||||
registry_url = try(secret.registry_url, "")
|
||||
dependencies = try(secret.dependencies, {})
|
||||
yc_sa_key_names = try(secret.yc_sa_key_names, {})
|
||||
custom_keys = try(secret.custom_keys, {})
|
||||
constant_keys = try(secret.constant_keys, {})
|
||||
random_keys = try(secret.random_keys, {})
|
||||
labels = try(secret.labels, {})
|
||||
annotations = try(secret.annotations, {})
|
||||
ignore_changes = try(secret.lifecycle.ignore_changes, false)
|
||||
}
|
||||
]
|
||||
secrets = local.secrets_input
|
||||
|
||||
s3_outputs = {
|
||||
bucket_name = dependency.s3.outputs.bucket_name
|
||||
@ -160,6 +199,7 @@ inputs = {
|
||||
|
||||
database_outputs_map = dependency.database.outputs.database_outputs_map
|
||||
rabbitmq_outputs_map = try(dependency.rabbitmq.outputs.rabbitmq_outputs_map, {})
|
||||
kafka_outputs_map = try(dependency.kafka_topics.outputs.kafka_outputs_map, {})
|
||||
valkey_outputs_map = dependency.valkey_users.outputs.valkey_outputs_map
|
||||
|
||||
constants = {
|
||||
|
||||
@ -16,6 +16,11 @@ remote_state {
|
||||
skip_metadata_api_check = true
|
||||
force_path_style = true
|
||||
skip_bucket_root_access = true
|
||||
# Object Storage бакет без versioning/SSE; свежий terragrunt иначе падает на проверке.
|
||||
skip_bucket_versioning = true
|
||||
skip_bucket_ssencryption = true
|
||||
skip_bucket_enforced_tls = true
|
||||
skip_bucket_public_access_blocking = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ locals {
|
||||
"valkey" = "Opaque"
|
||||
"yc_sa" = "Opaque"
|
||||
"rabbitmq" = "Opaque"
|
||||
"kafka" = "Opaque"
|
||||
"opaque" = "Opaque"
|
||||
}
|
||||
|
||||
@ -20,7 +21,7 @@ locals {
|
||||
}
|
||||
|
||||
secrets_map = {
|
||||
for idx, secret in var.secrets : secret.name => secret
|
||||
for secret in var.secrets : "${secret.namespace}/${secret.name}" => secret
|
||||
}
|
||||
|
||||
random_keys_flat = merge([
|
||||
@ -36,15 +37,15 @@ locals {
|
||||
|
||||
database_outputs_by_secret = {
|
||||
for name, secret in local.secrets_map : name => try(
|
||||
var.database_outputs_map["${try(secret.dependencies.cluster, "")}:${try(secret.dependencies.db, "")}:${try(secret.dependencies.user, "")}"],
|
||||
var.database_outputs_map["${try(secret.depends_on.cluster, "")}:${try(secret.depends_on.db, "")}:${try(secret.depends_on.user, "")}"],
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
valkey_dependency_keys = {
|
||||
for name, secret in local.secrets_map : name => {
|
||||
cluster = try(secret.dependencies.valkey_cluster, "") != "" ? try(secret.dependencies.valkey_cluster, "") : try(secret.dependencies.cluster, "")
|
||||
user = try(secret.dependencies.valkey_user, "") != "" ? try(secret.dependencies.valkey_user, "") : try(secret.dependencies.user, "")
|
||||
cluster = try(secret.depends_on.valkey_cluster, "") != "" ? try(secret.depends_on.valkey_cluster, "") : try(secret.depends_on.cluster, "")
|
||||
user = try(secret.depends_on.valkey_user, "") != "" ? try(secret.depends_on.valkey_user, "") : try(secret.depends_on.user, "")
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,21 +61,80 @@ locals {
|
||||
var.rabbitmq_outputs_map[
|
||||
format(
|
||||
"%s:%s:%s",
|
||||
try(secret.dependencies.rabbitmq_ref, "default"),
|
||||
try(secret.dependencies.rabbitmq_vhost, try(secret.dependencies.vhost, "")),
|
||||
try(secret.dependencies.rabbitmq_user, try(secret.dependencies.user, ""))
|
||||
try(secret.depends_on.rabbitmq_ref, "default"),
|
||||
try(secret.depends_on.rabbitmq_vhost, try(secret.depends_on.vhost, "")),
|
||||
try(secret.depends_on.rabbitmq_user, try(secret.depends_on.user, ""))
|
||||
)
|
||||
],
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
yc_sa_sources = {
|
||||
kafka_outputs_by_secret = {
|
||||
for name, secret in local.secrets_map : name => try(
|
||||
var.yc_service_accounts_map[try(secret.dependencies.service_account, "")],
|
||||
var.kafka_outputs_map[
|
||||
format(
|
||||
"%s:%s",
|
||||
try(secret.depends_on.kafka_ref, try(secret.depends_on.cluster_ref, try(secret.depends_on.cluster, ""))),
|
||||
try(secret.depends_on.kafka_user, try(secret.depends_on.user, ""))
|
||||
)
|
||||
],
|
||||
null
|
||||
)
|
||||
if secret.secret_type == "yc_sa"
|
||||
}
|
||||
|
||||
rabbitmq_default_credentials_by_secret = {
|
||||
for name, secret in local.secrets_map : name => local.rabbitmq_outputs_by_secret[name] == null ? {} : {
|
||||
host = local.rabbitmq_outputs_by_secret[name].host
|
||||
hostname = "${local.rabbitmq_outputs_by_secret[name].host}:${local.rabbitmq_outputs_by_secret[name].port}"
|
||||
port = local.rabbitmq_outputs_by_secret[name].port
|
||||
vhost = local.rabbitmq_outputs_by_secret[name].vhost
|
||||
user = local.rabbitmq_outputs_by_secret[name].username
|
||||
username = local.rabbitmq_outputs_by_secret[name].username
|
||||
password = local.rabbitmq_outputs_by_secret[name].password
|
||||
uri = local.rabbitmq_outputs_by_secret[name].uri
|
||||
management_endpoint = local.rabbitmq_outputs_by_secret[name].management_endpoint
|
||||
}
|
||||
if secret.type == "rabbitmq"
|
||||
}
|
||||
|
||||
rabbitmq_credentials_by_secret = {
|
||||
for name, secret in local.secrets_map : name => length(try(secret.credential_keys, {})) == 0 ? local.rabbitmq_default_credentials_by_secret[name] : {
|
||||
for key, source in try(secret.credential_keys, {}) : key => try(local.rabbitmq_default_credentials_by_secret[name][source], "")
|
||||
}
|
||||
if secret.type == "rabbitmq" && local.rabbitmq_outputs_by_secret[name] != null
|
||||
}
|
||||
|
||||
kafka_default_credentials_by_secret = {
|
||||
for name, secret in local.secrets_map : name => local.kafka_outputs_by_secret[name] == null ? {} : {
|
||||
host = local.kafka_outputs_by_secret[name].host
|
||||
hostname = local.kafka_outputs_by_secret[name].hostname
|
||||
port = local.kafka_outputs_by_secret[name].port
|
||||
username = local.kafka_outputs_by_secret[name].username
|
||||
user = local.kafka_outputs_by_secret[name].username
|
||||
password = local.kafka_outputs_by_secret[name].password
|
||||
sasl_mechanism = local.kafka_outputs_by_secret[name].sasl_mechanism
|
||||
security_protocol = local.kafka_outputs_by_secret[name].security_protocol
|
||||
bootstrap_server = local.kafka_outputs_by_secret[name].bootstrap_server
|
||||
bootstrap_servers = local.kafka_outputs_by_secret[name].bootstrap_servers
|
||||
bootstrap_servers_json = local.kafka_outputs_by_secret[name].bootstrap_servers_json
|
||||
}
|
||||
if secret.type == "kafka"
|
||||
}
|
||||
|
||||
kafka_credentials_by_secret = {
|
||||
for name, secret in local.secrets_map : name => length(try(secret.credential_keys, {})) == 0 ? local.kafka_default_credentials_by_secret[name] : {
|
||||
for key, source in try(secret.credential_keys, {}) : key => try(local.kafka_default_credentials_by_secret[name][source], "")
|
||||
}
|
||||
if secret.type == "kafka" && local.kafka_outputs_by_secret[name] != null
|
||||
}
|
||||
|
||||
yc_sa_sources = {
|
||||
for name, secret in local.secrets_map : name => try(
|
||||
var.yc_service_accounts_map[try(secret.depends_on.service_account, "")],
|
||||
null
|
||||
)
|
||||
if secret.type == "yc_sa"
|
||||
}
|
||||
|
||||
yc_sa_key_names_by_secret = {
|
||||
@ -82,12 +142,22 @@ locals {
|
||||
local.default_yc_sa_key_names,
|
||||
try(secret.yc_sa_key_names, {})
|
||||
)
|
||||
if secret.secret_type == "yc_sa"
|
||||
if secret.type == "yc_sa"
|
||||
}
|
||||
|
||||
secrets_data = {
|
||||
# Явные значения секрета (adopt 1:1 / статические). Приходят прямо из
|
||||
# infrastructure-secrets.yaml в поле data как map(key => base64(raw_value)).
|
||||
# k8s хранит .data уже в base64, поэтому base64decode даёт исходное значение байт-в-байт.
|
||||
secrets_data_override = {
|
||||
for name, secret in local.secrets_map : name => {
|
||||
for key, b64 in try(secret.data, {}) : key => base64decode(b64)
|
||||
}
|
||||
if length(try(secret.data, {})) > 0
|
||||
}
|
||||
|
||||
secrets_data_computed = {
|
||||
for name, secret in local.secrets_map : name =>
|
||||
secret.secret_type == "dockerconfigjson" ? {
|
||||
secret.type == "dockerconfigjson" ? {
|
||||
".dockerconfigjson" = jsonencode({
|
||||
auths = {
|
||||
"${secret.registry_url}" = {
|
||||
@ -97,13 +167,13 @@ locals {
|
||||
}
|
||||
}
|
||||
})
|
||||
} : secret.secret_type == "s3" ? {
|
||||
# Если указан конкретный bucket в dependencies - используем его, иначе fallback на s3_outputs
|
||||
access_key = try(var.s3_buckets_map[secret.dependencies.bucket].access_key, var.s3_outputs.access_key)
|
||||
secret_key = try(var.s3_buckets_map[secret.dependencies.bucket].secret_key, var.s3_outputs.secret_key)
|
||||
bucket = try(var.s3_buckets_map[secret.dependencies.bucket].bucket_name, var.s3_outputs.bucket_name)
|
||||
} : secret.type == "s3" ? {
|
||||
# Если указан конкретный bucket в depends_on - используем его, иначе fallback на s3_outputs
|
||||
access_key = try(var.s3_buckets_map[secret.depends_on.bucket].access_key, var.s3_outputs.access_key)
|
||||
secret_key = try(var.s3_buckets_map[secret.depends_on.bucket].secret_key, var.s3_outputs.secret_key)
|
||||
bucket = try(var.s3_buckets_map[secret.depends_on.bucket].bucket_name, var.s3_outputs.bucket_name)
|
||||
endpoint = try(var.constants.s3_endpoint, "https://storage.yandexcloud.net")
|
||||
} : secret.secret_type == "yc_sa" ? merge(
|
||||
} : secret.type == "yc_sa" ? merge(
|
||||
{
|
||||
(local.yc_sa_key_names_by_secret[name].access_key) = try(local.yc_sa_sources[name].access_key, "")
|
||||
(local.yc_sa_key_names_by_secret[name].secret_key) = try(local.yc_sa_sources[name].secret_key, "")
|
||||
@ -117,7 +187,7 @@ locals {
|
||||
{
|
||||
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
|
||||
}
|
||||
) : secret.secret_type == "database" && local.database_outputs_by_secret[name] != null ? merge(
|
||||
) : secret.type == "database" && local.database_outputs_by_secret[name] != null ? merge(
|
||||
# Базовые поля из database outputs
|
||||
{
|
||||
host = local.database_outputs_by_secret[name].host
|
||||
@ -136,7 +206,7 @@ locals {
|
||||
{
|
||||
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
|
||||
}
|
||||
) : secret.secret_type == "database_url" && local.database_outputs_by_secret[name] != null ? merge(
|
||||
) : secret.type == "database_url" && local.database_outputs_by_secret[name] != null ? merge(
|
||||
{
|
||||
database_url = format(
|
||||
"postgresql+asyncpg://%s:%s@%s:%s/%s",
|
||||
@ -154,7 +224,7 @@ locals {
|
||||
{
|
||||
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
|
||||
}
|
||||
) : secret.secret_type == "valkey" && local.valkey_outputs_by_secret[name] != null ? merge(
|
||||
) : secret.type == "valkey" && local.valkey_outputs_by_secret[name] != null ? merge(
|
||||
{
|
||||
cert = try(var.constants.valkey_ca, try(var.constants.postgres_ca, ""))
|
||||
host = local.valkey_outputs_by_secret[name].host
|
||||
@ -170,18 +240,17 @@ locals {
|
||||
{
|
||||
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
|
||||
}
|
||||
) : secret.secret_type == "rabbitmq" && local.rabbitmq_outputs_by_secret[name] != null ? merge(
|
||||
) : secret.type == "rabbitmq" && local.rabbitmq_outputs_by_secret[name] != null ? merge(
|
||||
local.rabbitmq_credentials_by_secret[name],
|
||||
try(secret.custom_keys, {}),
|
||||
{
|
||||
host = local.rabbitmq_outputs_by_secret[name].host
|
||||
hostname = "${local.rabbitmq_outputs_by_secret[name].host}:${local.rabbitmq_outputs_by_secret[name].port}"
|
||||
port = local.rabbitmq_outputs_by_secret[name].port
|
||||
vhost = local.rabbitmq_outputs_by_secret[name].vhost
|
||||
user = local.rabbitmq_outputs_by_secret[name].username
|
||||
username = local.rabbitmq_outputs_by_secret[name].username
|
||||
password = local.rabbitmq_outputs_by_secret[name].password
|
||||
uri = local.rabbitmq_outputs_by_secret[name].uri
|
||||
management_endpoint = local.rabbitmq_outputs_by_secret[name].management_endpoint
|
||||
for key, constant_name in try(secret.constant_keys, {}) : key => try(var.constants[constant_name], "")
|
||||
},
|
||||
{
|
||||
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
|
||||
}
|
||||
) : secret.type == "kafka" && local.kafka_outputs_by_secret[name] != null ? merge(
|
||||
local.kafka_credentials_by_secret[name],
|
||||
try(secret.custom_keys, {}),
|
||||
{
|
||||
for key, constant_name in try(secret.constant_keys, {}) : key => try(var.constants[constant_name], "")
|
||||
@ -200,18 +269,27 @@ locals {
|
||||
)
|
||||
}
|
||||
|
||||
# Если для секрета заданы явные значения (data) - используем их 1:1,
|
||||
# иначе собираем data из outputs/custom/constant/random как обычно.
|
||||
secrets_data = {
|
||||
for name, secret in local.secrets_map : name => (
|
||||
contains(keys(local.secrets_data_override), name)
|
||||
? local.secrets_data_override[name]
|
||||
: local.secrets_data_computed[name]
|
||||
)
|
||||
}
|
||||
|
||||
secrets_with_data = {
|
||||
for name, secret in local.secrets_map : name => {
|
||||
name = secret.name
|
||||
namespace = secret.namespace
|
||||
secret_type = secret.secret_type
|
||||
k8s_type = try(local.k8s_secret_type_map[secret.secret_type], "Opaque")
|
||||
type = secret.type
|
||||
k8s_type = try(local.k8s_secret_type_map[secret.type], "Opaque")
|
||||
labels = try(secret.labels, {})
|
||||
annotations = try(secret.annotations, {})
|
||||
ignore_changes = try(secret.ignore_changes, false)
|
||||
data = try(local.secrets_data[name], {})
|
||||
}
|
||||
if secret.secret_type != "rabbitmq" || local.rabbitmq_outputs_by_secret[name] != null
|
||||
}
|
||||
|
||||
secrets_with_ignore = {
|
||||
@ -254,7 +332,7 @@ resource "kubernetes_secret" "with_ignore" {
|
||||
|
||||
lifecycle {
|
||||
# Игнорируем data, type и metadata чтобы не было phantom updates
|
||||
ignore_changes = [data, type, metadata]
|
||||
ignore_changes = [data, type, metadata, wait_for_service_account_token]
|
||||
}
|
||||
|
||||
depends_on = [random_password.secrets]
|
||||
@ -276,7 +354,7 @@ resource "kubernetes_secret" "without_ignore" {
|
||||
|
||||
lifecycle {
|
||||
# type и metadata игнорируем чтобы не было phantom updates
|
||||
ignore_changes = [type, metadata]
|
||||
ignore_changes = [type, metadata, wait_for_service_account_token]
|
||||
}
|
||||
|
||||
depends_on = [random_password.secrets]
|
||||
|
||||
@ -1,16 +1,21 @@
|
||||
variable "secrets" {
|
||||
description = "Список секретов для создания"
|
||||
description = "Список секретов. Форма и данные декларируются в одном месте (infrastructure-secrets.yaml)."
|
||||
type = list(object({
|
||||
name = string
|
||||
namespace = string
|
||||
secret_type = string
|
||||
type = string
|
||||
registry_url = optional(string, "")
|
||||
dependencies = optional(object({
|
||||
data = optional(map(string), {}) # Явные значения (adopt/static), base64 как в k8s .data
|
||||
depends_on = optional(object({
|
||||
cluster = optional(string, "")
|
||||
cluster_ref = optional(string, "")
|
||||
db = optional(string, "")
|
||||
user = optional(string, "")
|
||||
valkey_cluster = optional(string, "")
|
||||
valkey_user = optional(string, "")
|
||||
kafka_ref = optional(string, "")
|
||||
kafka_user = optional(string, "")
|
||||
vhost = optional(string, "")
|
||||
bucket = optional(string, "") # Имя бакета для s3 секретов
|
||||
service_account = optional(string, "") # Имя YC Service Account для yc_sa секретов
|
||||
rabbitmq_ref = optional(string, "default")
|
||||
@ -28,9 +33,10 @@ variable "secrets" {
|
||||
length = optional(number, 32)
|
||||
special = optional(bool, false)
|
||||
})), {}) # Случайные ключи
|
||||
labels = optional(map(string), {})
|
||||
annotations = optional(map(string), {})
|
||||
ignore_changes = optional(bool, false)
|
||||
credential_keys = optional(map(string), {})
|
||||
labels = optional(map(string), {})
|
||||
annotations = optional(map(string), {})
|
||||
ignore_changes = optional(bool, false)
|
||||
}))
|
||||
}
|
||||
|
||||
@ -94,7 +100,7 @@ variable "valkey_outputs_map" {
|
||||
}
|
||||
|
||||
variable "rabbitmq_outputs_map" {
|
||||
description = "Outputs от модуля RabbitMQ, ключ = ref:vhost:user"
|
||||
description = "Outputs от модуля RabbitMQ, ключ = rabbitmq_ref:vhost:user"
|
||||
type = map(object({
|
||||
host = string
|
||||
port = string
|
||||
@ -107,6 +113,23 @@ variable "rabbitmq_outputs_map" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "kafka_outputs_map" {
|
||||
description = "Outputs от модуля Kafka, ключ = kafka_ref:user"
|
||||
type = map(object({
|
||||
host = string
|
||||
hostname = string
|
||||
port = string
|
||||
username = string
|
||||
password = string
|
||||
sasl_mechanism = string
|
||||
security_protocol = string
|
||||
bootstrap_server = string
|
||||
bootstrap_servers = string
|
||||
bootstrap_servers_json = string
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "constants" {
|
||||
description = "Константные значения (CA сертификаты, endpoints и т.д.)"
|
||||
type = map(string)
|
||||
|
||||
@ -7,3 +7,22 @@ output "user_names" {
|
||||
description = "Kafka users managed by this service manifest state."
|
||||
value = [for user in yandex_mdb_kafka_user.this : user.name]
|
||||
}
|
||||
|
||||
output "kafka_outputs_map" {
|
||||
description = "Map for k8s-secret module. Key format: clusterRef:user."
|
||||
value = {
|
||||
for key, item in local.user_clusters : key => {
|
||||
host = try(var.kafka_cluster_refs[item.cluster_ref].host, "")
|
||||
hostname = try(format("%s:%s", var.kafka_cluster_refs[item.cluster_ref].host, tostring(var.kafka_cluster_refs[item.cluster_ref].port)), "")
|
||||
port = tostring(try(var.kafka_cluster_refs[item.cluster_ref].port, 9091))
|
||||
username = item.user
|
||||
password = try(random_password.kafka_user[key].result, "")
|
||||
sasl_mechanism = try(var.kafka_cluster_refs[item.cluster_ref].sasl_mechanism, "SCRAM-SHA-512")
|
||||
security_protocol = try(var.kafka_cluster_refs[item.cluster_ref].security_protocol, "SASL_SSL")
|
||||
bootstrap_server = try(format("%s:%s", var.kafka_cluster_refs[item.cluster_ref].host, tostring(var.kafka_cluster_refs[item.cluster_ref].port)), "")
|
||||
bootstrap_servers = try(format("%s:%s", var.kafka_cluster_refs[item.cluster_ref].host, tostring(var.kafka_cluster_refs[item.cluster_ref].port)), "")
|
||||
bootstrap_servers_json = try(jsonencode([format("%s:%s", var.kafka_cluster_refs[item.cluster_ref].host, tostring(var.kafka_cluster_refs[item.cluster_ref].port))]), "[]")
|
||||
}
|
||||
}
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
@ -37,6 +37,10 @@ variable "kafka_cluster_refs" {
|
||||
description = "Environment clusterRef mapping from infrastructure.yaml."
|
||||
type = map(object({
|
||||
cluster_id = string
|
||||
host = optional(string, "")
|
||||
port = optional(number, 9091)
|
||||
sasl_mechanism = optional(string, "SCRAM-SHA-512")
|
||||
security_protocol = optional(string, "SASL_SSL")
|
||||
default_partitions = optional(number, 3)
|
||||
default_replication_factor = optional(number, 1)
|
||||
max_replication_factor = optional(number, 1)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
locals {
|
||||
rabbitmq_enabled = length(var.vhosts) + length(var.users) + length(var.permissions) + length(var.exchanges) + length(var.queues) + length(var.bindings) + length(var.policies) > 0
|
||||
rabbitmq_enabled = length(var.vhosts) + length(var.users) + length(var.permissions) + length(var.topic_permissions) + length(var.exchanges) + length(var.queues) + length(var.bindings) + length(var.policies) > 0
|
||||
|
||||
management_endpoint = local.rabbitmq_enabled ? var.management_endpoint : "http://127.0.0.1:15672"
|
||||
management_username = local.rabbitmq_enabled ? var.management_username : "noop"
|
||||
@ -7,7 +7,9 @@ locals {
|
||||
|
||||
vhosts = {
|
||||
for vhost in var.vhosts : vhost.name => {
|
||||
name = vhost.name
|
||||
name = vhost.name
|
||||
description = try(vhost.description, null)
|
||||
tracing = try(vhost.tracing, false)
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +32,14 @@ locals {
|
||||
}
|
||||
}
|
||||
|
||||
topic_permissions = {
|
||||
for permission in var.topic_permissions : "${permission.vhost}:${permission.user}" => {
|
||||
vhost = permission.vhost
|
||||
user = permission.user
|
||||
permissions = try(permission.permissions, [])
|
||||
}
|
||||
}
|
||||
|
||||
exchanges = {
|
||||
for exchange in var.exchanges : "${exchange.vhost}:${exchange.name}" => {
|
||||
name = exchange.name
|
||||
@ -52,7 +62,7 @@ locals {
|
||||
}
|
||||
|
||||
bindings = {
|
||||
for idx, binding in var.bindings : "${binding.vhost}:${binding.source}:${binding.destination_type}:${binding.destination}:${try(binding.routing_key, "")}:${idx}" => {
|
||||
for binding in var.bindings : "${binding.vhost}:${binding.source}:${binding.destination_type}:${binding.destination}:${try(binding.properties_key, format("%s:%s", try(binding.routing_key, ""), sha1(jsonencode(try(binding.arguments, {})))))}" => {
|
||||
vhost = binding.vhost
|
||||
source = binding.source
|
||||
destination = binding.destination
|
||||
@ -92,7 +102,9 @@ provider "rabbitmq" {
|
||||
resource "rabbitmq_vhost" "this" {
|
||||
for_each = local.vhosts
|
||||
|
||||
name = each.value.name
|
||||
name = each.value.name
|
||||
description = each.value.description
|
||||
tracing = each.value.tracing
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
@ -139,6 +151,26 @@ resource "rabbitmq_permissions" "this" {
|
||||
}
|
||||
}
|
||||
|
||||
resource "rabbitmq_topic_permissions" "this" {
|
||||
for_each = local.topic_permissions
|
||||
|
||||
user = rabbitmq_user.this[each.value.user].name
|
||||
vhost = rabbitmq_vhost.this[each.value.vhost].name
|
||||
|
||||
dynamic "permissions" {
|
||||
for_each = each.value.permissions
|
||||
content {
|
||||
exchange = permissions.value.exchange
|
||||
write = tostring(try(permissions.value.write, ""))
|
||||
read = tostring(try(permissions.value.read, ""))
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "rabbitmq_exchange" "this" {
|
||||
for_each = local.exchanges
|
||||
|
||||
@ -177,7 +209,7 @@ resource "rabbitmq_queue" "this" {
|
||||
resource "rabbitmq_binding" "this" {
|
||||
for_each = local.bindings
|
||||
|
||||
source = rabbitmq_exchange.this["${each.value.vhost}:${each.value.source}"].name
|
||||
source = each.value.source
|
||||
vhost = rabbitmq_vhost.this[each.value.vhost].name
|
||||
destination = each.value.destination
|
||||
destination_type = each.value.destination_type
|
||||
@ -187,6 +219,8 @@ resource "rabbitmq_binding" "this" {
|
||||
lifecycle {
|
||||
prevent_destroy = true
|
||||
}
|
||||
|
||||
depends_on = [rabbitmq_exchange.this, rabbitmq_queue.this]
|
||||
}
|
||||
|
||||
resource "rabbitmq_policy" "this" {
|
||||
|
||||
@ -5,19 +5,24 @@ output "vhosts" {
|
||||
|
||||
output "users" {
|
||||
description = "RabbitMQ users managed by this component."
|
||||
value = keys(rabbitmq_user.this)
|
||||
value = sort(concat(keys(rabbitmq_user.this), tolist(var.unmanaged_output_users)))
|
||||
}
|
||||
|
||||
output "rabbitmq_outputs_map" {
|
||||
description = "Map for k8s-secret module. Key format: default:vhost:user."
|
||||
value = {
|
||||
for key, item in local.user_vhosts : "default:${item.vhost}:${item.user}" => {
|
||||
for key, item in merge(local.user_vhosts, {
|
||||
for item in var.unmanaged_output_permissions : "${item.vhost}:${item.user}" => {
|
||||
vhost = item.vhost
|
||||
user = item.user
|
||||
}
|
||||
}) : "default:${item.vhost}:${item.user}" => {
|
||||
host = var.amqp_host
|
||||
port = tostring(var.amqp_port)
|
||||
vhost = item.vhost
|
||||
username = rabbitmq_user.this[item.user].name
|
||||
password = random_password.user[item.user].result
|
||||
uri = "amqp://${rabbitmq_user.this[item.user].name}:${urlencode(random_password.user[item.user].result)}@${var.amqp_host}:${var.amqp_port}/${urlencode(item.vhost)}"
|
||||
username = try(rabbitmq_user.this[item.user].name, item.user)
|
||||
password = try(random_password.user[item.user].result, "")
|
||||
uri = try("amqp://${rabbitmq_user.this[item.user].name}:${urlencode(random_password.user[item.user].result)}@${var.amqp_host}:${var.amqp_port}/${urlencode(item.vhost)}", "")
|
||||
management_endpoint = var.management_endpoint
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,43 +43,61 @@ variable "rabbitmq_policy" {
|
||||
|
||||
variable "vhosts" {
|
||||
description = "RabbitMQ vhosts declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "users" {
|
||||
description = "RabbitMQ users declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "permissions" {
|
||||
description = "RabbitMQ permissions declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "topic_permissions" {
|
||||
description = "RabbitMQ topic permissions declared in infrastructure.yaml."
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "exchanges" {
|
||||
description = "RabbitMQ exchanges declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "queues" {
|
||||
description = "RabbitMQ queues declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "bindings" {
|
||||
description = "RabbitMQ bindings declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "policies" {
|
||||
description = "RabbitMQ policies declared in infrastructure.yaml."
|
||||
type = list(any)
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "unmanaged_output_users" {
|
||||
description = "Existing RabbitMQ users intentionally not managed, kept only for output compatibility."
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "unmanaged_output_permissions" {
|
||||
description = "Existing RabbitMQ user/vhost pairs intentionally not managed, kept only for output compatibility."
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +37,15 @@ find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sort | whi
|
||||
|
||||
secrets_dependency_components=()
|
||||
needs_section=""
|
||||
# Для secrets: расшифровать sops-файл с live-значениями во временный файл вне артефактов
|
||||
# и передать путь в terragrunt через INFRA_SECRET_VALUES_FILE (требуется SOPS_AGE_KEY в CI).
|
||||
secrets_decrypt_before=""
|
||||
if [ "$component" = "secrets" ]; then
|
||||
for dep_component in namespace s3 database rabbitmq valkey-users; do
|
||||
secrets_decrypt_before=" - sops --decrypt infrastructure-secrets.yaml > /tmp/infra-secret-values.yaml
|
||||
- export INFRA_SECRET_VALUES_FILE=/tmp/infra-secret-values.yaml"
|
||||
fi
|
||||
if [ "$component" = "secrets" ]; then
|
||||
for dep_component in namespace s3 database rabbitmq kafka-topics; do
|
||||
if [ -f "live/${env}/${dep_component}/terragrunt.hcl" ]; then
|
||||
secrets_dependency_components+=("$dep_component")
|
||||
fi
|
||||
@ -59,6 +66,10 @@ ${dependencies_section}"
|
||||
validate_before_script=" - cd \$TG_ROOT
|
||||
- echo \"Working directory:\" && pwd
|
||||
- ls -la"
|
||||
if [ "$component" = "secrets" ]; then
|
||||
validate_before_script="${secrets_decrypt_before}
|
||||
${validate_before_script}"
|
||||
fi
|
||||
|
||||
case "$env" in
|
||||
stage) folder_var="YC_STAGE_FOLDER_ID" ;;
|
||||
@ -132,17 +143,24 @@ VALIDATE_JOB
|
||||
dependency_init_chain="${dependency_init_chain} && cd ../${dep_component} && terragrunt init -upgrade -reconfigure"
|
||||
fi
|
||||
done
|
||||
plan_before_script=" - ${dependency_init_chain} && cd ../secrets && ls -la"
|
||||
plan_before_script="${secrets_decrypt_before}
|
||||
- ${dependency_init_chain} && cd ../secrets && ls -la"
|
||||
fi
|
||||
if [ "$component" = "kafka-topics" ]; then
|
||||
plan_before_script=" - cd \$TG_ROOT && ls -la"
|
||||
fi
|
||||
|
||||
plan_allow_failure=""
|
||||
if [ "$env" = "stage" ] && [ "$component" = "secrets" ]; then
|
||||
plan_allow_failure=" allow_failure: true"
|
||||
fi
|
||||
|
||||
cat >> "$OUTPUT_FILE" << PLAN_JOB
|
||||
plan-${job_prefix}:
|
||||
stage: plan
|
||||
image: \$TERRAFORM_IMAGE_NAME
|
||||
${runner_tags}
|
||||
${plan_allow_failure}
|
||||
variables:
|
||||
TG_ROOT: "${full_component_dir}"
|
||||
ENVIRONMENT: "${env}"
|
||||
@ -184,7 +202,8 @@ PLAN_JOB
|
||||
dependency_init_chain="${dependency_init_chain} && cd ../${dep_component} && terragrunt init -upgrade -reconfigure"
|
||||
fi
|
||||
done
|
||||
apply_before_script=" - ${dependency_init_chain} && cd ../secrets && ls -la"
|
||||
apply_before_script="${secrets_decrypt_before}
|
||||
- ${dependency_init_chain} && cd ../secrets && ls -la"
|
||||
fi
|
||||
if [ "$component" = "kafka-topics" ]; then
|
||||
apply_before_script=" - cd \$TG_ROOT && ls -la"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user