From 3668b94644bada449908b2840b3857fb0ca98469 Mon Sep 17 00:00:00 2001 From: gitops_test Date: Wed, 23 Sep 2026 13:16:31 +0000 Subject: [PATCH] sync-modules: mirrored modules of release 3.2.0-F-feature-simpl-31669.676.1150352e +++ dbaas/README.md +++ dbaas/ovh/credentials.tf +++ dbaas/ovh/main.tf +++ dbaas/ovh/outputs.tf +++ dbaas/ovh/variables.tf +++ dbaas/ovh/versions.tf --- dbaas/README.md | 64 ++++++++++++++++++++++++++++++++ dbaas/ovh/credentials.tf | 28 ++++++++++++++ dbaas/ovh/main.tf | 46 +++++++++++++++++++++++ dbaas/ovh/outputs.tf | 34 +++++++++++++++++ dbaas/ovh/variables.tf | 79 ++++++++++++++++++++++++++++++++++++++++ dbaas/ovh/versions.tf | 12 ++++++ 6 files changed, 263 insertions(+) create mode 100644 dbaas/README.md create mode 100644 dbaas/ovh/credentials.tf create mode 100644 dbaas/ovh/main.tf create mode 100644 dbaas/ovh/outputs.tf create mode 100644 dbaas/ovh/variables.tf create mode 100644 dbaas/ovh/versions.tf diff --git a/dbaas/README.md b/dbaas/README.md new file mode 100644 index 0000000..035b5af --- /dev/null +++ b/dbaas/README.md @@ -0,0 +1,64 @@ +# dbaas module + +Provisions a managed database and generates the consumer access credentials. One submodule per cloud provider, selected by the provisioner from the cloud provider of the provisioning request: + +- `ovh`: an OVHcloud Managed PostgreSQL cluster (`ovh_cloud_project_database`) with one initial database and one application user, public by default, optionally attached to an existing vRack private network + +## Contract + +| Variable | Description | +|---|---| +| `deployment_id` | Unique deployment identifier, used in resource names | +| `cloud_account` | Cloud account (environment) name, second segment of the Vault credentials path, filled by the provisioner from the cloudAccount header via the `{CLOUD_ACCOUNT}` placeholder | +| `requester_unique_id` | Requester unique id, third segment of the Vault credentials path, filled by the provisioner from the requesterUniqueId header via the `{REQUESTER_UNIQUE_ID}` placeholder | +| `username` | Database user name, defaults to `simpl_app_user`, last segment of the Vault credentials path | +| `name_prefix` | Prefix for resource names, defaults to `simpl` | +| `region` | OVH macro region for the database nodes (for example SBG, GRA, DE), not the datacenter suffixed region used for instances | +| `project_id` | Optional override of the OVH project; by default the provider resolves it from the `OVH_CLOUD_PROJECT_SERVICE` variable of the runner environment, which the provisioner derives from the `OVH_PROJECT_ID` key of the cloud account configuration | +| `engine_version`, `plan`, `flavor` | PostgreSQL version and sizing of the managed offer | +| `database_name` | Name of the initial application database, defaults to `simpl_app_db` | +| `private_network_id`, `private_subnet_id` | Optional vRack attachment, both null for a public deployment | +| `allowed_ips` | CIDR blocks allowed to reach a public deployment | + +Cloud credentials are never module inputs, they reach the provider from the runner environment. + +Credentials, following the existing Vault contract: the OVHcloud managed database password is always generated server side (it cannot be set to an arbitrary value), read from the computed attribute of the user resource and written by the module to Vault: `secret/{provider}/{cloud_account}/{requester_unique_id}/{username}` with the keys username, password, host, port, database and a ready to use connection uri. No secret material is ever part of an output or of the user body. There is no admin sibling entry: day 2 credential renewal follows the same Terraform rotation flow as the other catalog modules, making OVHcloud regenerate the password and republishing the Vault entry in the same apply (the rotation marker input is added by the credential renewal ticket). The Vault provider configuration is generated by the provisioner next to the deployment script, and it authenticates through the Vault Kubernetes auth method with the service account of the Terraform runner, against a Vault role configured at installation time. + +Outputs: + +| Output | Description | +|---|---| +| `database_endpoint` | Service endpoint (hostname) of the PostgreSQL instance | +| `database_port` | Port of the PostgreSQL instance | +| `database_name` | Name of the initial database | +| `database_username` | Database user name | +| `database_cluster_id` | Id of the managed database cluster | +| `database_user_id` | Id of the database user | +| `credentials_ref` | Vault path of the consumer credentials entry | + +## Usage + +The provisioner rewrites the `{DBAAS_SOURCE}` placeholder to this module's location for the cloud provider of the request (each catalog module has a placeholder derived from its directory name), so a deployment script only declares: + +```hcl +module "dbaas" { + source = "{DBAAS_SOURCE}" + deployment_id = "{UUID}" + region = "" + cloud_account = "{CLOUD_ACCOUNT}" + requester_unique_id = "{REQUESTER_UNIQUE_ID}" + + plan = "essential" + flavor = "db1-4" +} + +output "database_endpoint" { + value = module.dbaas.database_endpoint +} + +output "credentials_ref" { + value = module.dbaas.credentials_ref +} +``` + +The root file must re-export the module outputs it wants published to the deployment outputs. diff --git a/dbaas/ovh/credentials.tf b/dbaas/ovh/credentials.tf new file mode 100644 index 0000000..d2ae5e1 --- /dev/null +++ b/dbaas/ovh/credentials.tf @@ -0,0 +1,28 @@ +// The consumer credential is generated by OVHcloud (server side password) +// and delivered only through Vault, never through outputs or the user body. +// The Vault entry follows the existing contract: +// secret/{provider}/{cloud_account}/{requester_unique_id}/{username} +// Only the Vault path of the entry is exposed as an output. There is no +// admin sibling entry: day 2 credential renewal follows the same Terraform +// rotation flow as the other catalog modules (the renewal workflow rewrites +// a rotation marker next to the deployment script and the apply regenerates +// the password, republishing this entry); the rotation marker input is +// added by the credential renewal ticket. + +locals { + database_host = one([for e in ovh_cloud_project_database.this.endpoints : e.domain if e.component == "postgresql"]) + database_port = one([for e in ovh_cloud_project_database.this.endpoints : e.port if e.component == "postgresql"]) +} + +resource "vault_kv_secret_v2" "consumer" { + mount = "secret" + name = "ovh/${var.cloud_account}/${var.requester_unique_id}/${var.username}" + data_json = jsonencode({ + username = ovh_cloud_project_database_postgresql_user.this.name + password = ovh_cloud_project_database_postgresql_user.this.password + host = local.database_host + port = local.database_port + database = ovh_cloud_project_database_database.this.name + uri = "postgres://${ovh_cloud_project_database_postgresql_user.this.name}:${ovh_cloud_project_database_postgresql_user.this.password}@${local.database_host}:${local.database_port}/${ovh_cloud_project_database_database.this.name}?sslmode=require" + }) +} diff --git a/dbaas/ovh/main.tf b/dbaas/ovh/main.tf new file mode 100644 index 0000000..92a0279 --- /dev/null +++ b/dbaas/ovh/main.tf @@ -0,0 +1,46 @@ +// OVHcloud Managed PostgreSQL. The cloud provider credentials and the +// project (OVH_CLOUD_PROJECT_SERVICE) reach the provider from the runner +// environment, never from module inputs. + +resource "ovh_cloud_project_database" "this" { + service_name = var.project_id + description = "${var.name_prefix}-postgresql-${var.deployment_id}" + engine = "postgresql" + version = var.engine_version + plan = var.plan + flavor = var.flavor + + nodes { + region = var.region + network_id = var.private_network_id + subnet_id = var.private_subnet_id + } + + dynamic "ip_restrictions" { + for_each = var.allowed_ips + content { + ip = ip_restrictions.value + } + } +} + +resource "ovh_cloud_project_database_database" "this" { + service_name = ovh_cloud_project_database.this.service_name + engine = ovh_cloud_project_database.this.engine + cluster_id = ovh_cloud_project_database.this.id + name = var.database_name +} + +// The user password is ALWAYS generated server side by OVHcloud, it cannot +// be set to an arbitrary value: it is read from the computed .password +// attribute and delivered only through the Vault entry. +resource "ovh_cloud_project_database_postgresql_user" "this" { + service_name = ovh_cloud_project_database.this.service_name + cluster_id = ovh_cloud_project_database.this.id + name = var.username + + // Serialize operations against the cluster: OVH DBaaS processes one + // operation at a time per cluster, and concurrent destroys (user + + // database in parallel) intermittently fail with API 500s. + depends_on = [ovh_cloud_project_database_database.this] +} diff --git a/dbaas/ovh/outputs.tf b/dbaas/ovh/outputs.tf new file mode 100644 index 0000000..7d7e437 --- /dev/null +++ b/dbaas/ovh/outputs.tf @@ -0,0 +1,34 @@ +output "database_endpoint" { + description = "Service endpoint (hostname) of the PostgreSQL instance" + value = local.database_host +} + +output "database_port" { + description = "Port of the PostgreSQL instance" + value = local.database_port +} + +output "database_name" { + description = "Name of the initial database" + value = ovh_cloud_project_database_database.this.name +} + +output "database_username" { + description = "Database user name" + value = ovh_cloud_project_database_postgresql_user.this.name +} + +output "database_cluster_id" { + description = "Id of the managed database cluster" + value = ovh_cloud_project_database.this.id +} + +output "database_user_id" { + description = "Id of the database user" + value = ovh_cloud_project_database_postgresql_user.this.id +} + +output "credentials_ref" { + description = "Vault path of the consumer credentials entry" + value = "secret/data/${vault_kv_secret_v2.consumer.name}" +} diff --git a/dbaas/ovh/variables.tf b/dbaas/ovh/variables.tf new file mode 100644 index 0000000..1ca6164 --- /dev/null +++ b/dbaas/ovh/variables.tf @@ -0,0 +1,79 @@ +variable "project_id" { + description = "OVH public cloud project id (service_name). Defaults to the OVH_CLOUD_PROJECT_SERVICE variable of the runner environment, derived from the cloud account configuration." + type = string + default = null +} + +variable "region" { + description = "OVH region for the database nodes. Managed databases use the macro region code (for example SBG, GRA, DE), not the datacenter suffixed region (SBG5) used for instances." + type = string +} + +variable "deployment_id" { + description = "Unique deployment identifier, used to build resource names" + type = string +} + +variable "cloud_account" { + description = "Cloud account (environment) name, second segment of the Vault credentials path" + type = string +} + +variable "requester_unique_id" { + description = "Requester unique id, third segment of the Vault credentials path" + type = string +} + +variable "name_prefix" { + description = "Prefix for all resource names" + type = string + default = "simpl" +} + +variable "username" { + description = "Database user name, last segment of the Vault credentials path" + type = string + default = "simpl_app_user" +} + +variable "engine_version" { + description = "PostgreSQL engine version to provision" + type = string + default = "16" +} + +variable "plan" { + description = "Service plan of the managed offer: essential, business or production" + type = string + default = "essential" +} + +variable "flavor" { + description = "Node flavor (compute/memory sizing) for the database cluster, for example db1-4" + type = string + default = "db1-4" +} + +variable "database_name" { + description = "Name of the initial application database to create on the cluster" + type = string + default = "simpl_app_db" +} + +variable "private_network_id" { + description = "OpenStack private network (vRack) id the database nodes should be attached to, null for a public deployment" + type = string + default = null +} + +variable "private_subnet_id" { + description = "Subnet id (within private_network_id) used by the database nodes, null for a public deployment" + type = string + default = null +} + +variable "allowed_ips" { + description = "CIDR blocks allowed to reach the cluster (IP restrictions), only relevant for public deployments" + type = list(string) + default = [] +} diff --git a/dbaas/ovh/versions.tf b/dbaas/ovh/versions.tf new file mode 100644 index 0000000..97b77d1 --- /dev/null +++ b/dbaas/ovh/versions.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + ovh = { + source = "ovh/ovh" + version = "2.3.0" + } + vault = { + source = "hashicorp/vault" + version = "4.8.0" + } + } +}