add analyzer db, user and secrets

This commit is contained in:
Work 2026-01-28 13:19:41 +03:00
parent 87b7f1080d
commit 0b66c81c3f
18 changed files with 300 additions and 316 deletions

2
.gitignore vendored
View File

@ -18,7 +18,7 @@ terraform.rc
.terragrunt-cache/ .terragrunt-cache/
terragrunt.hcl.backup terragrunt.hcl.backup
# Exception: commit lock files for dependencies to ensure consistent provider versions
!live/stage/s3/.terraform.lock.hcl !live/stage/s3/.terraform.lock.hcl
!live/stage/database/.terraform.lock.hcl !live/stage/database/.terraform.lock.hcl
!live/stage/namespace/.terraform.lock.hcl !live/stage/namespace/.terraform.lock.hcl

View File

@ -7,6 +7,11 @@ environments:
project: pulse project: pulse
annotations: annotations:
managed-by: terraform managed-by: terraform
- name: block-analyzer
labels:
project: block-analyzer
annotations:
managed-by: terraform
secrets: secrets:
- name: dockerhub - name: dockerhub
@ -28,7 +33,24 @@ environments:
db: pulse_db db: pulse_db
user: pulse user: pulse
lifecycle: lifecycle:
ignore_changes: false ignore_changes: true
- name: analyzer-postgresql-secret
namespace: block-analyzer
type: database
dependencies:
cluster: "c9qa2coo5ukgcg93fldm"
db: analyzer_db
user: analyzer
custom_keys:
sslmode: "verify-full"
random_keys:
full-host:
length: 32
special: false
lifecycle:
ignore_changes: true
- name: session-secret - name: session-secret
namespace: pulse namespace: pulse
@ -40,20 +62,20 @@ environments:
lifecycle: lifecycle:
ignore_changes: true ignore_changes: true
# Пример секрета для другой базы данных (test_db/test) # # Пример секрета для другой базы данных (test_db/test)
- name: test-db-secret # - name: test-db-secret
namespace: pulse # namespace: pulse
type: database # type: database
dependencies: # dependencies:
cluster: "c9qa2coo5ukgcg93fldm" # cluster: "c9qa2coo5ukgcg93fldm"
db: test_db # db: test_db
user: test # user: test
random_keys: # random_keys:
session_key: # session_key:
length: 32 # length: 32
special: false # special: false
lifecycle: # lifecycle:
ignore_changes: false # ignore_changes: false
buckets: buckets:
- name: pulse-stage - name: pulse-stage
@ -77,3 +99,61 @@ environments:
password_length: 32 password_length: 32
password_special: false password_special: false
conn_limit: 10 conn_limit: 10
- cluster_id: "c9qa2coo5ukgcg93fldm"
database:
name: analyzer_db
extensions:
- pg_stat_statements
user:
name: analyzer
password_length: 32
password_special: false
conn_limit: 10
permissions:
- pulse_db
- attachments_db
- automation_db
- bi_db
- bim_db
- bimv2_db
- camera_db
- camunda_db
- checklists_db
- comparator_db
- comparisons_db
- contracts_db
- document-understanding
- documentations_db
- drawings_db
- eav_db
- flow
- ingrad-notifications
- inspections_db
- issues_db
- keycloak
- mailer_db
- mcc
- metrics
- mvp-agents
- notes_db
- orchestrator
- pm-backend
- processing_db
- remarks_db
- resources_db
- rfi_db
- sarex-management
- sarex-compute
- sarex_db
- srx-data-engine_db
- stage-sarex
- subscriptions_db
- superset_db
- system-log_db
- test_db
- transmittals_db
- workspaces_db
- zitadel
- zitadel-new

View File

@ -1,26 +0,0 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:soK8Lt0SZ6dB+HsypFRDzuX/npqlMU6M0fvyaR1yW0k=",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.8.0"
constraints = "~> 3.1"
hashes = [
"h1:BYpqK2+ZHqNF9sauVugKJSeFWMCx11I/z/1lMplwUC0=",
]
}
provider "registry.terraform.io/yandex-cloud/yandex" {
version = "0.181.0"
constraints = "~> 0.100"
hashes = [
"h1:n5o9pvUkrGmunWVvZU4xGEUtfRXAG3KCvR9YrzsXc08=",
]
}

View File

@ -1,4 +1,3 @@
include "root" { include "root" {
path = find_in_parent_folders() path = find_in_parent_folders()
} }
@ -10,11 +9,10 @@ include "env" {
} }
locals { 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")) 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] env_config = local.infra_config.environments[local.env_name]
db_config = local.env_config.databases[0] # Первая БД из списка
} }
terraform { terraform {
@ -22,16 +20,11 @@ terraform {
} }
inputs = { inputs = {
cluster_id = local.db_config.cluster_id databases = local.env_config.databases
database_name = local.db_config.database.name
user_name = local.db_config.user.name yc_token = get_env("YC_TOKEN", "")
password_length = try(local.db_config.user.password_length, 32) yc_cloud_id = get_env("YC_CLOUD_ID", "")
password_special = try(local.db_config.user.password_special, false) yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
conn_limit = try(local.db_config.user.conn_limit, 10)
yc_token = get_env("YC_TOKEN", "")
yc_cloud_id = get_env("YC_CLOUD_ID", "")
yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
kubeconfig_path = get_env("KUBECONFIG", "") kubeconfig_path = get_env("KUBECONFIG", "")
kube_context = get_env("KUBE_CONTEXT", "") kube_context = get_env("KUBE_CONTEXT", "")
} }

View File

@ -1,26 +0,0 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:soK8Lt0SZ6dB+HsypFRDzuX/npqlMU6M0fvyaR1yW0k=",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.8.0"
constraints = "~> 3.1"
hashes = [
"h1:BYpqK2+ZHqNF9sauVugKJSeFWMCx11I/z/1lMplwUC0=",
]
}
provider "registry.terraform.io/yandex-cloud/yandex" {
version = "0.181.0"
constraints = "~> 0.100"
hashes = [
"h1:n5o9pvUkrGmunWVvZU4xGEUtfRXAG3KCvR9YrzsXc08=",
]
}

View File

@ -1,4 +1,3 @@
include "root" { include "root" {
path = find_in_parent_folders() path = find_in_parent_folders()
} }
@ -10,11 +9,10 @@ include "env" {
} }
locals { 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")) 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] env_config = local.infra_config.environments[local.env_name]
namespace = local.env_config.namespaces[0] # Первый namespace из списка
} }
terraform { terraform {
@ -22,13 +20,11 @@ terraform {
} }
inputs = { inputs = {
namespace_name = local.namespace.name namespaces = local.env_config.namespaces
labels = local.namespace.labels
annotations = local.namespace.annotations yc_token = get_env("YC_TOKEN", "")
yc_cloud_id = get_env("YC_CLOUD_ID", "")
yc_token = get_env("YC_TOKEN", "") yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
yc_cloud_id = get_env("YC_CLOUD_ID", "")
yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
kubeconfig_path = get_env("KUBECONFIG", "") kubeconfig_path = get_env("KUBECONFIG", "")
kube_context = get_env("KUBE_CONTEXT", "") kube_context = get_env("KUBE_CONTEXT", "")
} }

View File

@ -1,26 +0,0 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:soK8Lt0SZ6dB+HsypFRDzuX/npqlMU6M0fvyaR1yW0k=",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.8.0"
constraints = "~> 3.1"
hashes = [
"h1:BYpqK2+ZHqNF9sauVugKJSeFWMCx11I/z/1lMplwUC0=",
]
}
provider "registry.terraform.io/yandex-cloud/yandex" {
version = "0.181.0"
constraints = "~> 0.100"
hashes = [
"h1:n5o9pvUkrGmunWVvZU4xGEUtfRXAG3KCvR9YrzsXc08=",
]
}

View File

@ -1,26 +1,26 @@
# This file is maintained automatically by "terraform init". # This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates. # Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/kubernetes" { provider "registry.opentofu.org/hashicorp/kubernetes" {
version = "2.38.0" version = "2.38.0"
constraints = "~> 2.23" constraints = "~> 2.23"
hashes = [ hashes = [
"h1:soK8Lt0SZ6dB+HsypFRDzuX/npqlMU6M0fvyaR1yW0k=", "h1:ems+O2dA7atxMWpbtqIrsH7Oa+u+ERWSfpMaFnZPbh0=",
] ]
} }
provider "registry.terraform.io/hashicorp/random" { provider "registry.opentofu.org/hashicorp/random" {
version = "3.8.0" version = "3.8.1"
constraints = "~> 3.1" constraints = "~> 3.1"
hashes = [ hashes = [
"h1:BYpqK2+ZHqNF9sauVugKJSeFWMCx11I/z/1lMplwUC0=", "h1:LsYuJLZcYl1RiH7Hd3w90Ra5+k5cNqfdRUQXItkTI8Y=",
] ]
} }
provider "registry.terraform.io/yandex-cloud/yandex" { provider "registry.opentofu.org/yandex-cloud/yandex" {
version = "0.181.0" version = "0.127.0"
constraints = "~> 0.100" constraints = "~> 0.100"
hashes = [ hashes = [
"h1:n5o9pvUkrGmunWVvZU4xGEUtfRXAG3KCvR9YrzsXc08=", "h1:tms24VWADoryHdkGdH5A76fWG2k+9CrE9Je9yZMXUoI=",
] ]
} }

View File

@ -11,7 +11,11 @@ include "env" {
dependency "namespace" { dependency "namespace" {
config_path = "../namespace" config_path = "../namespace"
mock_outputs = { mock_outputs = {
name = "pulse" namespaces = {
"pulse" = { name = "pulse", id = "mock-id-1" }
"block-analyzer" = { name = "block-analyzer", id = "mock-id-2" }
}
names = ["pulse", "block-analyzer"]
} }
mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"]
} }
@ -29,10 +33,20 @@ dependency "s3" {
dependency "database" { dependency "database" {
config_path = "../database" config_path = "../database"
mock_outputs = { mock_outputs = {
host = "mock-host.example.com" database_outputs_map = {
database_name = "pulse_db" "c9qa2coo5ukgcg93fldm:pulse_db:pulse" = {
user_name = "pulse" host = "mock-host.example.com"
password = "mock-password" database_name = "pulse_db"
user_name = "pulse"
password = "mock-password"
}
"c9qa2coo5ukgcg93fldm:analyzer_db:analyzer" = {
host = "mock-host.example.com"
database_name = "analyzer_db"
user_name = "analyzer"
password = "mock-password"
}
}
} }
mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"] mock_outputs_allowed_terraform_commands = ["init", "validate", "plan"]
} }
@ -100,11 +114,10 @@ LpuQKbSbIERsmR+QqQ==
-----END CERTIFICATE----- -----END CERTIFICATE-----
EOT EOT
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")) 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] env_config = local.infra_config.environments[local.env_name]
secrets = local.env_config.secrets
} }
terraform { terraform {
@ -113,49 +126,42 @@ terraform {
inputs = { inputs = {
secrets = [ secrets = [
for secret in local.secrets : { for secret in local.env_config.secrets : {
name = secret.name name = secret.name
namespace = dependency.namespace.outputs.name namespace = secret.namespace
secret_type = secret.type secret_type = secret.type
registry_url = try(secret.registry_url, "") registry_url = try(secret.registry_url, "")
dependencies = try(secret.dependencies, {}) dependencies = try(secret.dependencies, {})
custom_keys = try(secret.custom_keys, {}) custom_keys = try(secret.custom_keys, {})
random_keys = try(secret.random_keys, {}) random_keys = try(secret.random_keys, {})
labels = try(secret.labels, {}) labels = try(secret.labels, {})
annotations = try(secret.annotations, {}) annotations = try(secret.annotations, {})
ignore_changes = try(secret.lifecycle.ignore_changes, false) ignore_changes = try(secret.lifecycle.ignore_changes, false)
} }
] ]
s3_outputs = { s3_outputs = {
bucket_name = dependency.s3.outputs.bucket_name bucket_name = dependency.s3.outputs.bucket_name
access_key = dependency.s3.outputs.access_key access_key = dependency.s3.outputs.access_key
secret_key = dependency.s3.outputs.secret_key secret_key = dependency.s3.outputs.secret_key
} }
database_outputs_map = { database_outputs_map = dependency.database.outputs.database_outputs_map
"${local.env_config.databases[0].cluster_id}:${local.env_config.databases[0].database.name}:${local.env_config.databases[0].user.name}" = {
host = dependency.database.outputs.host
database_name = dependency.database.outputs.database_name
user_name = dependency.database.outputs.user_name
password = dependency.database.outputs.password
}
}
constants = { constants = {
s3_endpoint = "https://storage.yandexcloud.net" s3_endpoint = "https://storage.yandexcloud.net"
postgres_port = "6432" postgres_port = "6432"
postgres_ca = local.yc_postgresql_ca_cert postgres_ca = local.yc_postgresql_ca_cert
} }
env_vars = { env_vars = {
DOCKER_REGISTRY_USERNAME = get_env("DOCKER_REGISTRY_USERNAME", "") DOCKER_REGISTRY_USERNAME = get_env("DOCKER_REGISTRY_USERNAME", "")
DOCKER_REGISTRY_PASSWORD = get_env("DOCKER_REGISTRY_PASSWORD", "") DOCKER_REGISTRY_PASSWORD = get_env("DOCKER_REGISTRY_PASSWORD", "")
} }
yc_token = get_env("YC_TOKEN", "") yc_token = get_env("YC_TOKEN", "")
yc_cloud_id = get_env("YC_CLOUD_ID", "") yc_cloud_id = get_env("YC_CLOUD_ID", "")
yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", "")) yc_folder_id = get_env("YC_STAGE_FOLDER_ID", get_env("YC_FOLDER_ID", ""))
kubeconfig_path = get_env("KUBECONFIG", "") kubeconfig_path = get_env("KUBECONFIG", "")
kube_context = get_env("KUBE_CONTEXT", "") kube_context = get_env("KUBE_CONTEXT", "")
} }

View File

@ -14,7 +14,7 @@ remote_state {
skip_region_validation = true skip_region_validation = true
skip_credentials_validation = true skip_credentials_validation = true
skip_metadata_api_check = true skip_metadata_api_check = true
force_path_style = true use_path_style = true
skip_bucket_root_access = true skip_bucket_root_access = true
} }
} }

View File

@ -1,13 +1,15 @@
# Создание namespace в Kubernetes # Создание namespace'ов в Kubernetes
resource "kubernetes_namespace" "pulse" { resource "kubernetes_namespace" "this" {
for_each = { for ns in var.namespaces : ns.name => ns }
metadata { metadata {
name = var.namespace_name name = each.value.name
labels = merge( labels = merge(
{ {
name = var.namespace_name name = each.value.name
}, },
var.labels try(each.value.labels, {})
) )
annotations = var.annotations annotations = try(each.value.annotations, {})
} }
} }

View File

@ -1,9 +1,14 @@
output "name" { output "namespaces" {
description = "Name of the created namespace" description = "Map of created namespaces (name => namespace data)"
value = kubernetes_namespace.pulse.metadata[0].name value = {
for name, ns in kubernetes_namespace.this : name => {
name = ns.metadata[0].name
id = ns.id
}
}
} }
output "id" { output "names" {
description = "ID of the created namespace" description = "List of namespace names"
value = kubernetes_namespace.pulse.id value = [for name, ns in kubernetes_namespace.this : ns.metadata[0].name]
} }

View File

@ -1,16 +1,8 @@
variable "namespace_name" { variable "namespaces" {
description = "Name of the Kubernetes namespace" description = "List of Kubernetes namespaces to create"
type = string type = list(object({
} name = string
labels = optional(map(string), {})
variable "labels" { annotations = optional(map(string), {})
description = "Labels for the namespace" }))
type = map(string)
default = {}
}
variable "annotations" {
description = "Annotations for the namespace"
type = map(string)
default = {}
} }

View File

@ -1,6 +1,14 @@
locals { locals {
# Маппинг наших типов на Kubernetes типы
k8s_secret_type_map = {
"dockerconfigjson" = "kubernetes.io/dockerconfigjson"
"database" = "Opaque"
"s3" = "Opaque"
"opaque" = "Opaque"
}
secrets_map = { secrets_map = {
for idx, secret in var.secrets : secret.name => secret for idx, secret in var.secrets : secret.name => secret
} }
@ -40,16 +48,23 @@ locals {
secret_key = var.s3_outputs.secret_key secret_key = var.s3_outputs.secret_key
bucket = var.s3_outputs.bucket_name bucket = var.s3_outputs.bucket_name
endpoint = try(var.constants.s3_endpoint, "https://storage.yandexcloud.net") endpoint = try(var.constants.s3_endpoint, "https://storage.yandexcloud.net")
} : secret.secret_type == "database" && local.database_outputs_by_secret[name] != null ? merge({ } : secret.secret_type == "database" && local.database_outputs_by_secret[name] != null ? merge(
host = local.database_outputs_by_secret[name].host # Базовые поля из database outputs
port = try(var.constants.postgres_port, "6432") {
database = local.database_outputs_by_secret[name].database_name host = local.database_outputs_by_secret[name].host
user = local.database_outputs_by_secret[name].user_name port = try(var.constants.postgres_port, "6432")
password = local.database_outputs_by_secret[name].password database = local.database_outputs_by_secret[name].database_name
"ca.crt" = try(var.constants.postgres_ca, "") username = local.database_outputs_by_secret[name].user_name
}, { password = local.database_outputs_by_secret[name].password
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result "ca.crt" = try(var.constants.postgres_ca, "")
}) : merge( },
# Кастомные статические поля
try(secret.custom_keys, {}),
# Рандомные поля
{
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
}
) : merge(
try(secret.custom_keys, {}), try(secret.custom_keys, {}),
{ {
for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result for key, config in try(secret.random_keys, {}) : key => random_password.secrets["${name}:${key}"].result
@ -59,13 +74,14 @@ locals {
secrets_with_data = { secrets_with_data = {
for name, secret in local.secrets_map : name => { for name, secret in local.secrets_map : name => {
name = secret.name name = secret.name
namespace = secret.namespace namespace = secret.namespace
secret_type = secret.secret_type secret_type = secret.secret_type
labels = try(secret.labels, {}) k8s_type = try(local.k8s_secret_type_map[secret.secret_type], "Opaque")
annotations = try(secret.annotations, {}) labels = try(secret.labels, {})
annotations = try(secret.annotations, {})
ignore_changes = try(secret.ignore_changes, false) ignore_changes = try(secret.ignore_changes, false)
data = try(local.secrets_data[name], {}) data = try(local.secrets_data[name], {})
} }
} }
@ -99,14 +115,15 @@ resource "kubernetes_secret" "with_ignore" {
annotations = try(each.value.annotations, {}) annotations = try(each.value.annotations, {})
} }
type = each.value.secret_type type = each.value.k8s_type
data = each.value.data data = each.value.data
lifecycle { lifecycle {
ignore_changes = [data] # type игнорируем чтобы не пересоздавать из-за opaque->Opaque
ignore_changes = [data, type]
} }
depends_on = [random_password.secrets] depends_on = [random_password.secrets]
} }
@ -120,9 +137,14 @@ resource "kubernetes_secret" "without_ignore" {
annotations = try(each.value.annotations, {}) annotations = try(each.value.annotations, {})
} }
type = each.value.secret_type type = each.value.k8s_type
data = each.value.data data = each.value.data
lifecycle {
# type игнорируем чтобы не пересоздавать из-за opaque->Opaque
ignore_changes = [type]
}
depends_on = [random_password.secrets] depends_on = [random_password.secrets]
} }

View File

@ -1,41 +1,53 @@
locals {
databases_map = { for db in var.databases : "${db.cluster_id}:${db.database.name}:${db.user.name}" => db }
}
resource "random_password" "user_password" { resource "random_password" "user_password" {
length = var.password_length for_each = local.databases_map
special = var.password_special
length = try(each.value.user.password_length, 32)
special = try(each.value.user.password_special, false)
upper = true upper = true
lower = true lower = true
numeric = true numeric = true
} }
data "yandex_mdb_postgresql_cluster" "existing" { data "yandex_mdb_postgresql_cluster" "existing" {
cluster_id = var.cluster_id for_each = toset([for db in var.databases : db.cluster_id])
cluster_id = each.value
} }
resource "yandex_mdb_postgresql_user" "this" { resource "yandex_mdb_postgresql_user" "this" {
cluster_id = var.cluster_id for_each = local.databases_map
name = var.user_name
password = random_password.user_password.result
conn_limit = var.conn_limit
cluster_id = each.value.cluster_id
name = each.value.user.name
password = random_password.user_password[each.key].result
conn_limit = try(each.value.user.conn_limit, 10)
# permissions только для ДРУГИХ существующих БД
# на свою БД user получает права автоматически как owner
dynamic "permission" { dynamic "permission" {
for_each = length(var.permissions) > 0 ? var.permissions : [] for_each = distinct(compact(try(each.value.user.permissions, [])))
content { content {
database_name = permission.value.database_name database_name = permission.value
} }
} }
} }
resource "yandex_mdb_postgresql_database" "this" { resource "yandex_mdb_postgresql_database" "this" {
cluster_id = var.cluster_id for_each = local.databases_map
name = var.database_name
owner = yandex_mdb_postgresql_user.this.name cluster_id = each.value.cluster_id
lc_collate = var.lc_collate name = each.value.database.name
lc_type = var.lc_type owner = yandex_mdb_postgresql_user.this[each.key].name
lc_collate = try(each.value.database.lc_collate, "en_US.UTF-8")
lc_type = try(each.value.database.lc_type, "en_US.UTF-8")
depends_on = [yandex_mdb_postgresql_user.this] depends_on = [yandex_mdb_postgresql_user.this]
dynamic "extension" { dynamic "extension" {
for_each = var.extensions for_each = try(each.value.database.extensions, [])
content { content {
name = extension.value name = extension.value
} }

View File

@ -1,35 +1,27 @@
output "cluster_id" { output "databases" {
description = "ID кластера PostgreSQL" description = "Map of created databases (cluster_id:db_name:user_name => data)"
value = var.cluster_id value = {
for key, db in local.databases_map : key => {
cluster_id = db.cluster_id
host = try(data.yandex_mdb_postgresql_cluster.existing[db.cluster_id].host[0].fqdn, "")
port = 6432
database_name = yandex_mdb_postgresql_database.this[key].name
user_name = yandex_mdb_postgresql_user.this[key].name
password = random_password.user_password[key].result
}
}
sensitive = true
} }
output "cluster_fqdn" { output "database_outputs_map" {
description = "FQDN кластера PostgreSQL" description = "Map for secrets module (cluster_id:db_name:user_name => credentials)"
value = data.yandex_mdb_postgresql_cluster.existing.host[0].fqdn value = {
} for key, db in local.databases_map : key => {
host = try(data.yandex_mdb_postgresql_cluster.existing[db.cluster_id].host[0].fqdn, "")
output "host" { database_name = yandex_mdb_postgresql_database.this[key].name
description = "Host адрес кластера" user_name = yandex_mdb_postgresql_user.this[key].name
value = data.yandex_mdb_postgresql_cluster.existing.host[0].fqdn password = random_password.user_password[key].result
} }
}
output "port" { sensitive = true
description = "Порт кластера"
value = 6432
}
output "database_name" {
description = "Имя базы данных"
value = yandex_mdb_postgresql_database.this.name
}
output "user_name" {
description = "Имя пользователя"
value = yandex_mdb_postgresql_user.this.name
}
output "password" {
description = "Пароль пользователя"
value = random_password.user_password.result
sensitive = true
} }

View File

@ -1,58 +1,19 @@
variable "cluster_id" { variable "databases" {
description = "ID существующего PostgreSQL кластера в Yandex Cloud" description = "List of databases to create"
type = string
}
variable "database_name" {
description = "Имя базы данных"
type = string
}
variable "user_name" {
description = "Имя пользователя"
type = string
}
variable "password_length" {
description = "Длина пароля"
type = number
default = 32
}
variable "password_special" {
description = "Использовать специальные символы в пароле"
type = bool
default = false
}
variable "lc_collate" {
description = "LC_COLLATE для базы данных"
type = string
default = "en_US.UTF-8"
}
variable "lc_type" {
description = "LC_TYPE для базы данных"
type = string
default = "en_US.UTF-8"
}
variable "extensions" {
description = "Список расширений PostgreSQL"
type = list(string)
default = []
}
variable "permissions" {
description = "Права доступа пользователя"
type = list(object({ type = list(object({
database_name = string cluster_id = string
database = object({
name = string
lc_collate = optional(string, "en_US.UTF-8")
lc_type = optional(string, "en_US.UTF-8")
extensions = optional(list(string), [])
})
user = object({
name = string
password_length = optional(number, 32)
password_special = optional(bool, false)
conn_limit = optional(number, 10)
permissions = optional(list(string), [])
})
})) }))
default = []
}
variable "conn_limit" {
description = "Лимит подключений для пользователя"
type = number
default = 10
} }

View File

@ -99,6 +99,7 @@ validate-${job_prefix}:
- export TF_PLUGIN_CACHE_DIR=/root/.terraform.d/plugin-cache - export TF_PLUGIN_CACHE_DIR=/root/.terraform.d/plugin-cache
script: script:
- env | grep -E "(S3_|AWS_)" || echo "No S3/AWS vars found" - env | grep -E "(S3_|AWS_)" || echo "No S3/AWS vars found"
- rm -f .terraform.lock.hcl
- terragrunt init -upgrade -reconfigure - terragrunt init -upgrade -reconfigure
- terragrunt validate - terragrunt validate
rules: rules: