test terragrunt ci

This commit is contained in:
Work 2026-01-28 15:05:41 +03:00
parent 51dce6b708
commit d6300bfa80
6 changed files with 89 additions and 83 deletions

View File

@ -63,6 +63,22 @@ environments:
lifecycle:
ignore_changes: true
- name: testci-postgresql-secret
namespace: testci
type: database
dependencies:
cluster: "c9qa2coo5ukgcg93fldm"
db: testci_db
user: testci
lifecycle:
ignore_changes: true
custom_keys:
sslmode: "verify-full"
random_keys:
full-host:
length: 32
special: false
- name: session-secret
namespace: pulse

View File

@ -1,4 +1,3 @@
include "root" {
path = find_in_parent_folders()
}
@ -10,26 +9,20 @@ include "env" {
}
locals {
repo_root = try(get_repo_root(), "${get_terragrunt_dir()}/../../..")
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_name = basename(dirname(get_terragrunt_dir()))
env_config = local.infra_config.environments[local.env_name]
bucket = local.env_config.buckets[0] # Первый bucket из списка
}
terraform {
source = "${get_terragrunt_dir()}/../../../modules//yc-s3"
}
inputs = {
bucket_name = local.bucket.name
folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
acl = try(local.bucket.acl, "private")
versioning_enabled = try(local.bucket.versioning.enabled, false)
cors_enabled = try(local.bucket.cors.enabled, false)
cors_config = try(local.bucket.cors, {})
buckets = local.env_config.buckets
folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
yc_token = get_env("YC_TOKEN", "")
yc_service_account_key_file = get_env("YC_SERVICE_ACCOUNT_KEY_FILE", "")
yc_cloud_id = get_env("YC_CLOUD_ID", "")

View File

@ -29,13 +29,11 @@ resource "yandex_mdb_postgresql_user" "this" {
password = random_password.user_password[each.key].result
conn_limit = try(each.value.user.conn_limit, 10)
# Permission на свою БД + дополнительные permissions
# Yandex API требует явный permission даже для владельца
# Permissions только на ДРУГИЕ существующие БД
# Permission на свою БД НЕ добавляем - owner получает доступ автоматически
# и БД ещё не существует на момент создания user
dynamic "permission" {
for_each = distinct(concat(
[each.value.database.name],
try(each.value.user.permissions, [])
))
for_each = distinct(compact(try(each.value.user.permissions, [])))
content {
database_name = permission.value
}

View File

@ -1,35 +1,44 @@
# Универсальный модуль для создания S3 бакета
# Не знает про конкретные сущности
# Универсальный модуль для создания S3 бакетов
# Создание сервисного аккаунта для S3
locals {
buckets_map = { for bucket in var.buckets : bucket.name => bucket }
}
# Создание сервисного аккаунта для каждого бакета
resource "yandex_iam_service_account" "sa" {
name = "${var.bucket_name}-sa"
description = "Service account for ${var.bucket_name} bucket"
for_each = local.buckets_map
name = "${each.key}-sa"
description = "Service account for ${each.key} bucket"
}
# Назначение роли storage.editor сервисному аккаунту
resource "yandex_resourcemanager_folder_iam_member" "storage_editor" {
for_each = local.buckets_map
folder_id = var.folder_id
role = "storage.editor"
member = "serviceAccount:${yandex_iam_service_account.sa.id}"
member = "serviceAccount:${yandex_iam_service_account.sa[each.key].id}"
}
# Создание статического ключа доступа
resource "yandex_iam_service_account_static_access_key" "sa_key" {
service_account_id = yandex_iam_service_account.sa.id
description = "Static access key for ${var.bucket_name} bucket"
for_each = local.buckets_map
service_account_id = yandex_iam_service_account.sa[each.key].id
description = "Static access key for ${each.key} bucket"
}
# Создание S3 бакета
# Bucket создается от имени текущего пользователя (YC_TOKEN), не сервисного аккаунта
# Ключи сервисного аккаунта используются только для доступа к bucket после создания
resource "yandex_storage_bucket" "this" {
bucket = var.bucket_name
acl = var.acl
for_each = local.buckets_map
bucket = each.key
acl = each.value.acl
# Версионирование объектов
dynamic "versioning" {
for_each = var.versioning_enabled ? [1] : []
for_each = try(each.value.versioning.enabled, false) ? [1] : []
content {
enabled = true
}
@ -37,13 +46,13 @@ resource "yandex_storage_bucket" "this" {
# Настройка CORS
dynamic "cors_rule" {
for_each = var.cors_enabled ? [1] : []
for_each = try(each.value.cors.enabled, false) ? [1] : []
content {
allowed_headers = var.cors_config.allowed_headers
allowed_methods = var.cors_config.allowed_methods
allowed_origins = var.cors_config.allowed_origins
expose_headers = try(var.cors_config.expose_headers, [])
max_age_seconds = try(var.cors_config.max_age_seconds, 3600)
allowed_headers = try(each.value.cors.allowed_headers, [])
allowed_methods = try(each.value.cors.allowed_methods, [])
allowed_origins = try(each.value.cors.allowed_origins, [])
expose_headers = try(each.value.cors.expose_headers, [])
max_age_seconds = try(each.value.cors.max_age_seconds, 3600)
}
}
}

View File

@ -1,21 +1,31 @@
# Для обратной совместимости - первый бакет
output "bucket_name" {
description = "Name of the created bucket"
value = yandex_storage_bucket.this.bucket
description = "Name of the first bucket (for backward compatibility)"
value = length(var.buckets) > 0 ? yandex_storage_bucket.this[var.buckets[0].name].bucket : ""
}
output "access_key" {
description = "Access key for S3 bucket"
value = yandex_iam_service_account_static_access_key.sa_key.access_key
description = "Access key for the first bucket (for backward compatibility)"
value = length(var.buckets) > 0 ? yandex_iam_service_account_static_access_key.sa_key[var.buckets[0].name].access_key : ""
sensitive = true
}
output "secret_key" {
description = "Secret key for S3 bucket"
value = yandex_iam_service_account_static_access_key.sa_key.secret_key
description = "Secret key for the first bucket (for backward compatibility)"
value = length(var.buckets) > 0 ? yandex_iam_service_account_static_access_key.sa_key[var.buckets[0].name].secret_key : ""
sensitive = true
}
output "service_account_id" {
description = "ID of the service account"
value = yandex_iam_service_account.sa.id
# Новые outputs - map всех бакетов
output "buckets" {
description = "Map of all buckets (name => data)"
value = {
for name, bucket in yandex_storage_bucket.this : name => {
bucket_name = bucket.bucket
access_key = yandex_iam_service_account_static_access_key.sa_key[name].access_key
secret_key = yandex_iam_service_account_static_access_key.sa_key[name].secret_key
service_account_id = yandex_iam_service_account.sa[name].id
}
}
sensitive = true
}

View File

@ -1,43 +1,23 @@
variable "bucket_name" {
description = "Name of the S3 bucket"
type = string
variable "buckets" {
description = "List of S3 buckets to create"
type = list(object({
name = string
acl = optional(string, "private")
versioning = optional(object({
enabled = bool
}), { enabled = false })
cors = optional(object({
enabled = optional(bool, false)
allowed_headers = optional(list(string), [])
allowed_methods = optional(list(string), [])
allowed_origins = optional(list(string), [])
expose_headers = optional(list(string), [])
max_age_seconds = optional(number, 3600)
}), { enabled = false })
}))
}
variable "folder_id" {
description = "Yandex Cloud folder ID"
type = string
}
variable "acl" {
description = "ACL для бакета"
type = string
default = "private"
}
variable "versioning_enabled" {
description = "Enable versioning for the bucket"
type = bool
default = false
}
variable "cors_enabled" {
description = "Enable CORS for the bucket"
type = bool
default = false
}
variable "cors_config" {
description = "CORS configuration"
type = object({
allowed_headers = list(string)
allowed_methods = list(string)
allowed_origins = list(string)
expose_headers = optional(list(string))
max_age_seconds = optional(number)
})
default = {
allowed_headers = []
allowed_methods = []
allowed_origins = []
}
}