From 794d2df51c48e8d5a64a7144a0acbe9a79c5995d Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:54:10 +0530 Subject: [PATCH 01/39] feat(ferretdb): cluster and database design for FerretDB --- modules/ferretdb/cluster.tf | 130 ++++++++++++++++++++++ modules/ferretdb/cluster_image_catalog.tf | 21 ++++ modules/ferretdb/database.tf | 44 ++++++++ 3 files changed, 195 insertions(+) create mode 100644 modules/ferretdb/cluster.tf create mode 100644 modules/ferretdb/cluster_image_catalog.tf create mode 100644 modules/ferretdb/database.tf diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf new file mode 100644 index 0000000..0c5540e --- /dev/null +++ b/modules/ferretdb/cluster.tf @@ -0,0 +1,130 @@ +// CloudNative PG Cluster +resource "kubernetes_manifest" "cluster" { + manifest = { + "apiVersion" = "postgresql.cnpg.io/v1" + "kind" = "Cluster" + "metadata" = { + "labels" = { + "app" = var.app_name + "component" = "cluster" + } + "name" = var.cluster_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + } + "spec" = { + "inheritedMetadata" = { + "labels" = { + "garage-access" = true + } + } + "postgresUID" = 999 + "postgresGID" = 999 + "topologySpreadConstraints" = [ + { + "maxSkew" = 1 + "topologyKey" = "kubernetes.io/hostname" + "whenUnsatisfiable" = "DoNotSchedule" + "labelSelector" = { + "matchLabels" = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + ] + "imageCatalogRef" = { + "apiGroup" = "postgresql.cnpg.io", + "kind" = "ClusterImageCatalog" + "name" = kubernetes_manifest.ferret_cluster_image_catalog.manifest.metadata.name + "major" = var.cluster_postgresql_version + } + "description" = "PostgreSQL Cluster for storing relational data" + "enableSuperuserAccess" = true + "instances" = var.cluster_size + // Required postgresql configuration for DocumentDB + "postgresql" = { + "parameters" = { + "shared_preload_libraries" = "pg_documentdb_core,pg_documentdb" + "search_path" = "\"$user\", public, documentdb_api, documentdb_core" + } + } + "managed" = { + "roles" = concat([ + { + "bypassrls" = false + "comment" = "ferret user for postgresql" + "connectionLimit" = -1 + "createdb" = true + "createrole" = true + "ensure" = "present" + "inherit" = true + "login" = true + "name" = "ferret" + "passwordSecret" = { + "name" = kubernetes_secret.ferret_database_credentials.metadata[0].name + } + "replication" = false + "superuser" = false + }, + ], local.managed_roles) + } + "primaryUpdateStrategy" = "unsupervised" + "resources" = { + "limits" = { + "cpu" = "500m" + "memory" = "1Gi" + } + "requests" = { + "cpu" = "500m" + "memory" = "1Gi" + } + } + "startDelay" = 300 + "storage" = { + "pvcTemplate" = { + "accessModes" = [ + "ReadWriteOnce", + ] + "resources" = { + "requests" = { + "storage" = "5Gi" + } + } + "storageClassName" = "local-path" + "volumeMode" = "Filesystem" + } + "size" = "5Gi" + } + "certificates" = { + "serverTLSSecret" = kubernetes_manifest.server_certificate.manifest.spec.secretName + "serverCASecret" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName + "clientCASecret" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName + "replicationTLSSecret" = kubernetes_manifest.client_streaming_replica_certificate.manifest.spec.secretName + } + "plugins" = [ + { + "name" = "barman-cloud.cloudnative-pg.io" + "isWALArchiver" = true + "parameters" = { + "barmanObjectName" = kubernetes_manifest.barman_object_store.manifest.metadata.name + } + } + ] + } + } + + // Fields to ignore changes for + computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "10m" + update = "10m" + delete = "10m" + } +} diff --git a/modules/ferretdb/cluster_image_catalog.tf b/modules/ferretdb/cluster_image_catalog.tf new file mode 100644 index 0000000..a5db5c6 --- /dev/null +++ b/modules/ferretdb/cluster_image_catalog.tf @@ -0,0 +1,21 @@ +resource "kubernetes_manifest" "ferret_cluster_image_catalog" { + manifest = { + "apiVersion" : "postgresql.cnpg.io/v1" + "kind" : "ClusterImageCatalog" + "metadata" = { + "name" = "ferret" + } + "spec" = { + "images" = [ + { + "major" = 16 + "image" = "ghcr.io/ferretdb/postgres-documentdb:16-0.107.0-ferretdb-2.7.0@sha256:43d5279693ed8ad77a18f981af47e5b36b9497bb6caa288093a4e10493ac9e5e" + }, + { + "major" = 17 + "image" = "ghcr.io/ferretdb/postgres-documentdb:17-0.107.0-ferretdb-2.7.0@sha256:2386795ec2aa7ae559304361979f1dc5708d383ee9020ae63dadc2940dfe58f7" + }, + ] + } + } +} diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf new file mode 100644 index 0000000..21c43f7 --- /dev/null +++ b/modules/ferretdb/database.tf @@ -0,0 +1,44 @@ +// Database Configuration for Ferret +resource "kubernetes_manifest" "ferret_database" { + manifest = { + "apiVersion" : "postgresql.cnpg.io/v1" + "kind" : "Database" + "metadata" = { + "name" = "ferret" + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "cluster" + } + } + + "spec" = { + "databaseReclaimPolicy" : "delete" + "name" : "ferret" + "owner" : "ferret" + "cluster" = { + "name" = kubernetes_manifest.cluster.manifest.metadata.name + } + "extensions" = { + "vector" = { + "ensure" = "present" + } + "pg_documentdb" = { + "ensure" = "present" + } + } + } + } + + wait { + fields = { + "status.applied" = "true" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} From 1d54b0b221592a104209278017b51d0e072ccac4 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:55:09 +0530 Subject: [PATCH 02/39] feat(ferretdb): certs, netpol, secrets and variables update for ferretdb --- modules/ferretdb/certificates.tf | 347 ++++++++++++++++++++++++++++++ modules/ferretdb/networkpolicy.tf | 166 ++++++++++++++ modules/ferretdb/secrets.tf | 90 ++++++++ modules/ferretdb/variables.tf | 150 +++++++++++++ 4 files changed, 753 insertions(+) create mode 100644 modules/ferretdb/certificates.tf create mode 100644 modules/ferretdb/networkpolicy.tf create mode 100644 modules/ferretdb/secrets.tf create mode 100644 modules/ferretdb/variables.tf diff --git a/modules/ferretdb/certificates.tf b/modules/ferretdb/certificates.tf new file mode 100644 index 0000000..30c9a20 --- /dev/null +++ b/modules/ferretdb/certificates.tf @@ -0,0 +1,347 @@ +// Fetch Garage Certificate Authority for PITR Backups +resource "kubernetes_secret" "garage_certificate_authority" { + metadata { + name = var.garage_certificate_authority + namespace = kubernetes_namespace.namespace.metadata[0].name + + labels = { + app = var.app_name + component = "secret" + } + + annotations = { + "reflector.v1.k8s.emberstack.com/reflects" : "${var.garage_namespace}/${var.garage_certificate_authority}" + } + } + + data = { + "tls.crt" = "" + "tls.key" = "" + "ca.crt" = "" + } + + type = "kubernetes.io/tls" + + lifecycle { + ignore_changes = [metadata[0].annotations] + } +} + +# --------------- POSTGRESQL SERVER CERTIFICATES CONFIGURATION --------------- # +// Certificate Authority to be used with PostgreSQL Server +resource "kubernetes_manifest" "server_certificate_authority" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Certificate" + "metadata" = { + "name" = var.server_certificate_authority_name + "namespace" = var.namespace + "labels" = { + "app" = var.app_name + "component" = "certificate-authority" + } + } + "spec" = { + "isCA" = true + "subject" = { + "organizations" = [var.organization_name] + "countries" = [var.country_name] + "organizationalUnits" = ["PostgreSQL"] + } + "commonName" = var.server_certificate_authority_name + "secretName" = var.server_certificate_authority_name + "secretTemplate" = { + "annotations" = { + "reflector.v1.k8s.emberstack.com/reflection-allowed" = "true" + "reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces" = length(var.clients) == 0 ? "keycloak" : "keycloak,${join(",", local.access_namespaces)}" + } + } + "duration" = "70128h" + "privateKey" = { + "algorithm" = "ECDSA" + "size" = 256 + } + "issuerRef" = { + "name" = "${var.cluster_issuer_name}" + "kind" = "ClusterIssuer" + "group" = "cert-manager.io" + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +// Issuer to be used with PostgreSQL Server +resource "kubernetes_manifest" "server_issuer" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Issuer" + "metadata" = { + "name" = var.server_issuer_name + "namespace" = "${var.namespace}" + "labels" = { + "app" = var.app_name + "component" = "issuer" + } + } + "spec" = { + "ca" = { + "secretName" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +// Certificate for PostgreSQL Server +resource "kubernetes_manifest" "server_certificate" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Certificate" + "metadata" = { + "name" = var.server_certificate_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "certificate" + } + } + "spec" = { + "usages" : ["server auth"] + "dnsNames" = [ + "${var.cluster_name}-rw", + "${var.cluster_name}-rw.${kubernetes_namespace.namespace.metadata[0].name}", + "${var.cluster_name}-rw.${kubernetes_namespace.namespace.metadata[0].name}.svc", + "${var.cluster_name}-r", + "${var.cluster_name}-r.${kubernetes_namespace.namespace.metadata[0].name}", + "${var.cluster_name}-r.${kubernetes_namespace.namespace.metadata[0].name}.svc", + "${var.cluster_name}-ro", + "${var.cluster_name}-ro.${kubernetes_namespace.namespace.metadata[0].name}", + "${var.cluster_name}-ro.${kubernetes_namespace.namespace.metadata[0].name}.svc" + ] + "subject" = { + "organizations" = [var.organization_name] + "countries" = [var.country_name] + "organizationalUnits" = ["postgres"] + } + "secretName" = var.server_certificate_name + "issuerRef" = { + "name" = kubernetes_manifest.server_issuer.manifest.metadata.name + } + } + } + + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +# --------------- POSTGRESQL CLIENT CERTIFICATES CONFIGURATION --------------- # +// Certificate Authority to be used with PostgreSQL Client +resource "kubernetes_manifest" "client_certificate_authority" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Certificate" + "metadata" = { + "name" = var.client_certificate_authority_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "certificate-authority" + } + } + "spec" = { + "isCA" = true + "subject" = { + "organizations" = [var.organization_name] + "countries" = [var.country_name] + "organizationalUnits" = ["PostgreSQL"] + } + "commonName" = var.client_certificate_authority_name + "secretName" = var.client_certificate_authority_name + "duration" = "70128h" + "privateKey" = { + "algorithm" = "ECDSA" + "size" = 256 + } + "issuerRef" = { + "name" = "${var.cluster_issuer_name}" + "kind" = "ClusterIssuer" + "group" = "cert-manager.io" + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +// Issuer to be used with PostgreSQL Client +resource "kubernetes_manifest" "client_issuer" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Issuer" + "metadata" = { + "name" = var.client_issuer_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "issuer" + } + } + "spec" = { + "ca" = { + "secretName" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +// Certificate for Streaming Replica +resource "kubernetes_manifest" "client_streaming_replica_certificate" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Certificate" + "metadata" = { + "name" = var.client_streaming_replica_certificate_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "certificate" + } + } + "spec" = { + "usages" : ["client auth"] + "subject" = { + "organizations" = [var.organization_name] + "countries" = [var.country_name] + "organizationalUnits" = ["PostgreSQL"] + } + "commonName" = "streaming_replica" + "secretName" = var.client_streaming_replica_certificate_name + "issuerRef" = { + "name" = kubernetes_manifest.client_issuer.manifest.metadata.name + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} + +// Certificate for Ferret User +resource "kubernetes_manifest" "client_ferret_certificate" { + manifest = { + "apiVersion" = "cert-manager.io/v1" + "kind" = "Certificate" + "metadata" = { + "name" = "postgresql-ferret-client-certificate" + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "certificate" + } + } + "spec" = { + "usages" : ["client auth"] + "subject" = { + "organizations" = [var.organization_name] + "countries" = [var.country_name] + "organizationalUnits" = ["PostgreSQL"] + } + "commonName" = "ferret" + "secretName" = "postgresql-ferret-client-certificate" + "additionalOutputFormats" = [ + { + "type" : "DER" + } + ] + "privateKey" = { + "encoding" = "PKCS8" + } + "issuerRef" = { + "name" = kubernetes_manifest.client_issuer.manifest.metadata.name + } + } + } + + wait { + condition { + type = "Ready" + status = "True" + } + } + + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} diff --git a/modules/ferretdb/networkpolicy.tf b/modules/ferretdb/networkpolicy.tf new file mode 100644 index 0000000..3824148 --- /dev/null +++ b/modules/ferretdb/networkpolicy.tf @@ -0,0 +1,166 @@ +resource "kubernetes_network_policy" "cnpg_network_policy" { + metadata { + name = "cnpg-network-policy" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "networkpolicy" + } + } + + spec { + pod_selector { + match_labels = { + "cnpg.io/cluster" = var.cluster_name + } + } + + policy_types = ["Ingress", "Egress"] + + # -------------- INGRESS RULES -------------- # + # Rule 1: Allow ingress from other CNPG Operator + ingress { + from { + namespace_selector { + match_labels = { + "kubernetes.io/metadata.name" = "cnpg-system" + } + } + + pod_selector { + match_labels = { + "app.kubernetes.io/name" = "cloudnative-pg" + } + } + } + + ports { + protocol = "TCP" + port = 8000 + } + ports { + protocol = "TCP" + port = 5432 + } + } + + # Rule 2: Allow ingress from other CNPG pods + ingress { + from { + pod_selector { + match_labels = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + + ports { + protocol = "TCP" + port = 5432 + } + ports { + protocol = "TCP" + port = 8000 + } + } + + # Rule 3: Allow ingress from allowed pods in trusted namespaces + ingress { + from { + namespace_selector { + match_expressions { + key = "kubernetes.io/metadata.name" + operator = "In" + values = concat(local.access_namespaces, ["keycloak", kubernetes_namespace.namespace.metadata[0].name]) + } + } + + pod_selector { + match_labels = { + "ferret-access" = true + } + } + } + + ports { + protocol = "TCP" + port = 5432 + } + } + + # -------------- EGRESS RULES -------------- # + # Rule 1: Allow egress to other CNPG pods + egress { + to { + pod_selector { + match_labels = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + + ports { + protocol = "TCP" + port = 5433 + } + ports { + protocol = "TCP" + port = 5432 + } + ports { + protocol = "TCP" + port = 8000 + } + } + + # Rule 2: Allow egress to Garage S3 Cluster for PITR + egress { + to { + namespace_selector { + match_labels = { + "kubernetes.io/metadata.name" = var.garage_namespace + } + } + } + + ports { + protocol = "TCP" + port = 3940 + } + } + + # Rule 3: Allow DNS resolution to KubeDNS + egress { + to { + namespace_selector { + match_labels = { + "kubernetes.io/metadata.name" = "kube-system" + } + } + pod_selector { + match_labels = { + "k8s-app" = "kube-dns" + } + } + } + ports { + protocol = "UDP" + port = 53 + } + } + + + # Rule 4: Allow Egress to Kubernetes API + egress { + to { + ip_block { + cidr = "${var.kubernetes_api_ip}/32" + } + } + ports { + protocol = var.kubernetes_api_protocol + port = var.kubernetes_api_port + } + } + } +} diff --git a/modules/ferretdb/secrets.tf b/modules/ferretdb/secrets.tf new file mode 100644 index 0000000..815bb83 --- /dev/null +++ b/modules/ferretdb/secrets.tf @@ -0,0 +1,90 @@ +// Garage Credentials for storing PostgreSQL PITR Backups +resource "kubernetes_secret" "garage_configuration" { + metadata { + name = var.garage_configuration + namespace = kubernetes_namespace.namespace.metadata[0].name + + labels = { + app = var.app_name + component = "secret" + } + + annotations = { + "reflector.v1.k8s.emberstack.com/reflects" = "${var.garage_namespace}/${var.garage_configuration}" + } + } + + data = { + KEY_NAME = "" + ACCESS_KEY_ID = "" + SECRET_ACCESS_KEY = "" + S3_REGION = "" + } + + type = "Opaque" + + lifecycle { + ignore_changes = [metadata[0].annotations] + } +} + +// Database credentials configuration for Ferret +resource "random_password" "ferret_password" { + length = 20 + lower = true + numeric = true + special = false +} + +resource "kubernetes_secret" "ferret_database_credentials" { + metadata { + name = "credentials-ferret" + namespace = kubernetes_namespace.namespace.metadata[0].name + + labels = { + app = var.app_name + component = "secret" + } + } + + data = { + "username" = "ferret" + "password" = random_password.keycloak_password.result + } + + type = "kubernetes.io/basic-auth" +} + +// Database credentials configuration for all clients +resource "random_password" "client_password" { + count = length(var.clients) + length = 20 + lower = true + numeric = true + special = false +} + +resource "kubernetes_secret" "client_database_credentials" { + count = length(var.clients) + metadata { + name = "credentials-${var.clients[count.index].user}" + namespace = kubernetes_namespace.namespace.metadata[0].name + + labels = { + app = var.app_name + component = "secret" + } + + annotations = { + "reflector.v1.k8s.emberstack.com/reflection-allowed" = "true" + "reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces" = var.clients[count.index].namespace + } + } + + data = { + "username" = var.clients[count.index].user + "password" = random_password.client_password[count.index].result + } + + type = "kubernetes.io/basic-auth" +} diff --git a/modules/ferretdb/variables.tf b/modules/ferretdb/variables.tf new file mode 100644 index 0000000..5cb3e10 --- /dev/null +++ b/modules/ferretdb/variables.tf @@ -0,0 +1,150 @@ +# --------------- GENERAL VARIABLES --------------- # +variable "app_name" { + description = "App name for deploying Ferret Database" + type = string + default = "ferret" +} + +variable "organization_name" { + description = "Organization name for deploying Ferret Database" + type = string + default = "cloud" +} + +variable "country_name" { + description = "Country name for deploying Ferret Database" + type = string + default = "India" +} + +# --------------- NAMESPACE VARIABLES --------------- # +variable "namespace" { + description = "Namespace to be used for deploying Ferret Database" + type = string + default = "ferret" +} + +variable "garage_namespace" { + description = "Namespace for the Garage Deployment for storing PITR Backups" + type = string + nullable = false +} + +# --------------- CERTIFICATE VARIABLES --------------- # +variable "garage_certificate_authority" { + description = "Name of the Certificate Authority associated with the Garage Storage Solution" + type = string + nullable = false +} + +variable "cluster_issuer_name" { + description = "Name for the Cluster Issuer to be used to generate internal self signed certificates" + type = string + nullable = false +} + +variable "server_certificate_authority_name" { + description = "Name of the Certificate Authority to be used with Ferret Server" + type = string + default = "ferretdb-server-certificate-authority" +} + +variable "server_issuer_name" { + description = "Name of the Issuer to be used with Ferret Server" + type = string + default = "ferretdb-server-issuer" +} + +variable "server_certificate_name" { + description = "Name of the Certificate to be used with Ferret Server" + type = string + default = "ferretdb-server-certificate" +} + +variable "client_certificate_authority_name" { + description = "Name of the Certificate Authority to be used with Ferret Client" + type = string + default = "ferretdb-client-certificate-authority" +} + +variable "client_issuer_name" { + description = "Name of the Issuer to be used with Ferret Client" + type = string + default = "ferretdb-client-issuer" +} + +variable "client_streaming_replica_certificate_name" { + description = "Name of the Certificate to be used with Ferret Streaming Replica Client" + type = string + default = "ferretdb-streaming-replica-client-certificate" +} + +# --------------- USER CONFIGURATION VARIABLES --------------- # +variable "clients" { + description = "Object List of clients who need databases and users to be configured for" + type = list(object({ + namespace = string + user = string + database = string + derRequired = bool + privateKeyEncoding = string + })) + default = [] + validation { + condition = length([ + for object in var.clients : true + if contains(["PKCS1", "PKCS8"], object.privateKeyEncoding) + ]) == length(var.clients) + error_message = "Encoding Value is either PKCS1 or PKCS8" + } +} + +# --------------- CLUSTER VARIABLES VARIABLES --------------- # +variable "garage_configuration" { + description = "Garage Configuration for storing PITR Backups" + type = string + nullable = false +} + +variable "cluster_name" { + description = "Name of the Ferret Database Cluster to be created" + type = string + default = "ferret-postgresql-cluster" +} + +variable "cluster_postgresql_version" { + description = "Version of Ferret Database to use and deploy" + type = number + default = 17 +} + +variable "cluster_size" { + description = "Number of pods to deploy for the Ferret Cluster" + type = number + default = 2 +} + +variable "backup_bucket_name" { + description = "Name of the bucket for storing PITR Backups in Garage" + type = string + nullable = false +} + +# --------------- NETWORK POLICY VARIABLES --------------- # +variable "kubernetes_api_ip" { + description = "IP Address for the Kubernetes API" + type = string + nullable = false +} + +variable "kubernetes_api_protocol" { + description = "Protocol for the Kubernetes API" + type = string + nullable = false +} + +variable "kubernetes_api_port" { + description = "Port for the Kubernetes API" + type = number + nullable = false +} From 9d2e842e69c4d8f9bd3f6146d116cf9a9ba9aaa1 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:55:33 +0530 Subject: [PATCH 03/39] feat(ferretdb): ferretdb module completion (hopefully) --- modules/ferretdb/README.md | 101 ++++++++++++++++++++++++ modules/ferretdb/locals.tf | 19 +++++ modules/ferretdb/namespace.tf | 10 +++ modules/ferretdb/objectstore.tf | 48 +++++++++++ modules/ferretdb/outputs.tf | 17 ++++ modules/ferretdb/poddisruptionbudget.tf | 21 +++++ 6 files changed, 216 insertions(+) create mode 100644 modules/ferretdb/README.md create mode 100644 modules/ferretdb/locals.tf create mode 100644 modules/ferretdb/namespace.tf create mode 100644 modules/ferretdb/objectstore.tf create mode 100644 modules/ferretdb/outputs.tf create mode 100644 modules/ferretdb/poddisruptionbudget.tf diff --git a/modules/ferretdb/README.md b/modules/ferretdb/README.md new file mode 100644 index 0000000..ad72e77 --- /dev/null +++ b/modules/ferretdb/README.md @@ -0,0 +1,101 @@ +## necronizer's cloud cloudnative pg module + +OpenTofu Module to deploy [Cloudnative PG](https://cloudnative-pg.io/) PostgreSQL Database on the Kubernetes Cluster + +Required Modules to deploy Cloudnative PG PostgreSQL Database: +1. [Helm](../helm) +2. [Cluster Issuer](../cluster-issuer) +3. [Garage](../garage) + +## Providers + +| Name | Version | +|------|---------| +| [kubernetes](#provider\_kubernetes) | 2.38.0 | +| [random](#provider\_random) | 3.7.2 | + +## Resources + +| Name | Type | +|------|------| +| [kubernetes_config_map.nginx_conf](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource | +| [kubernetes_config_map.pgadmin_servers_configuration](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource | +| [kubernetes_deployment.pgadmin](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | +| [kubernetes_ingress_v1.api_ingress](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/ingress_v1) | resource | +| [kubernetes_manifest.barman_object_store](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.client_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.client_certificates](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.client_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.client_keycloak_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.client_streaming_replica_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.cluster](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.cluster_image_catalog](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.databases](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.ingress_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.keycloak_database](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.pgadmin_internal_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.public_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.server_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.server_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.server_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_namespace.namespace](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | +| [kubernetes_network_policy.cnpg_network_policy](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/network_policy) | resource | +| [kubernetes_pod_disruption_budget_v1.cnpg_pdb](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/pod_disruption_budget_v1) | resource | +| [kubernetes_secret.client_database_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.cloudflare_token](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.garage_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.garage_configuration](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.keycloak_database_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.pgadmin_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_service.pgadmin4](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service) | resource | +| [random_password.client_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [random_password.keycloak_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [random_password.pgadmin_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [acme\_server](#input\_acme\_server) | URL for the ACME Server to be used, defaults to production URL for LetsEncrypt | `string` | `"https://acme-v02.api.letsencrypt.org/directory"` | no | +| [app\_name](#input\_app\_name) | App name for deploying PostgreSQL Database | `string` | `"postgres"` | no | +| [backup\_bucket\_name](#input\_backup\_bucket\_name) | Name of the bucket for storing PITR Backups in Garage | `string` | n/a | yes | +| [client\_certificate\_authority\_name](#input\_client\_certificate\_authority\_name) | Name of the Certificate Authority to be used with PostgreSQL Client | `string` | `"postgresql-client-certificate-authority"` | no | +| [client\_issuer\_name](#input\_client\_issuer\_name) | Name of the Issuer to be used with PostgreSQL Client | `string` | `"postgresql-client-issuer"` | no | +| [client\_streaming\_replica\_certificate\_name](#input\_client\_streaming\_replica\_certificate\_name) | Name of the Certificate to be used with PostgreSQL Streaming Replica Client | `string` | `"postgresql-streaming-replica-client-certificate"` | no | +| [clients](#input\_clients) | Object List of clients who need databases and users to be configured for |
list(object({
namespace = string
user = string
database = string
derRequired = bool
privateKeyEncoding = string
}))
| `[]` | no | +| [cloudflare\_email](#input\_cloudflare\_email) | Email for generating Ingress Certificates to be associated with PGAdmin | `string` | n/a | yes | +| [cloudflare\_issuer\_name](#input\_cloudflare\_issuer\_name) | Name of the Cloudflare Issuer to be associated with PGAdmin | `string` | `"cnpg-cloudflare-issuer"` | no | +| [cloudflare\_token](#input\_cloudflare\_token) | Token for generating Ingress Certificates to be associated with PGAdmin | `string` | n/a | yes | +| [cluster\_issuer\_name](#input\_cluster\_issuer\_name) | Name for the Cluster Issuer to be used to generate internal self signed certificates | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | Name of the PostgreSQL Database Cluster to be created | `string` | `"postgresql-cluster"` | no | +| [cluster\_postgresql\_version](#input\_cluster\_postgresql\_version) | Version of PostgreSQL Database to use and deploy | `number` | `17` | no | +| [cluster\_size](#input\_cluster\_size) | Number of pods to deploy for the PostgreSQL Cluster | `number` | `2` | no | +| [country\_name](#input\_country\_name) | Country name for deploying PostgreSQL Database | `string` | `"India"` | no | +| [domain](#input\_domain) | Domain for which Ingress Certificate is to be generated for | `string` | n/a | yes | +| [garage\_certificate\_authority](#input\_garage\_certificate\_authority) | Name of the Certificate Authority associated with the Garage Storage Solution | `string` | n/a | yes | +| [garage\_configuration](#input\_garage\_configuration) | Garage Configuration for storing PITR Backups | `string` | n/a | yes | +| [garage\_namespace](#input\_garage\_namespace) | Namespace for the Garage Deployment for storing PITR Backups | `string` | n/a | yes | +| [host\_name](#input\_host\_name) | Host name for which Ingress Certificate is to be generated for | `string` | `"sql"` | no | +| [image](#input\_image) | Docker image to be used for deployment of PGAdmin | `string` | `"pgadmin4"` | no | +| [ingress\_certificate\_name](#input\_ingress\_certificate\_name) | Name of the Ingress Certificate to be associated with PGAdmin | `string` | `"pgadmin-ingress-certificate"` | no | +| [kubernetes\_api\_ip](#input\_kubernetes\_api\_ip) | IP Address for the Kubernetes API | `string` | n/a | yes | +| [kubernetes\_api\_port](#input\_kubernetes\_api\_port) | Port for the Kubernetes API | `number` | n/a | yes | +| [kubernetes\_api\_protocol](#input\_kubernetes\_api\_protocol) | Protocol for the Kubernetes API | `string` | n/a | yes | +| [namespace](#input\_namespace) | Namespace to be used for deploying PostgreSQL Database | `string` | `"postgres"` | no | +| [organization\_name](#input\_organization\_name) | Organization name for deploying PostgreSQL Database | `string` | `"cloud"` | no | +| [proxy\_image](#input\_proxy\_image) | Docker image to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"nginx"` | no | +| [proxy\_repository](#input\_proxy\_repository) | Repository to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"docker.io/library"` | no | +| [proxy\_tag](#input\_proxy\_tag) | Docker tag to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"1.29.0"` | no | +| [repository](#input\_repository) | Repository to be used for deployment of PGAdmin | `string` | `"docker.io/dpage"` | no | +| [server\_certificate\_authority\_name](#input\_server\_certificate\_authority\_name) | Name of the Certificate Authority to be used with PostgreSQL Server | `string` | `"postgresql-server-certificate-authority"` | no | +| [server\_certificate\_name](#input\_server\_certificate\_name) | Name of the Certificate to be used with PostgreSQL Server | `string` | `"postgresql-server-certificate"` | no | +| [server\_issuer\_name](#input\_server\_issuer\_name) | Name of the Issuer to be used with PostgreSQL Server | `string` | `"postgresql-server-issuer"` | no | +| [tag](#input\_tag) | Docker tag to be used for deployment of PGAdmin | `string` | `"9.7.0"` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [cluster\_name](#output\_cluster\_name) | Name of the CNPG PostgreSQL Cluster | +| [namespace](#output\_namespace) | Namespace where the PostgreSQL Database is deployed in | +| [server-certificate-authority](#output\_server-certificate-authority) | Certificate Authority being used with PostgreSQL Database | diff --git a/modules/ferretdb/locals.tf b/modules/ferretdb/locals.tf new file mode 100644 index 0000000..560de2b --- /dev/null +++ b/modules/ferretdb/locals.tf @@ -0,0 +1,19 @@ +locals { + access_namespaces = [for config in var.clients : config.namespace] + managed_roles = [for secret in kubernetes_secret.client_database_credentials : { + "bypassrls" = false + "comment" = "${secret.data.username} user for postgresql" + "connectionLimit" = -1 + "createdb" = true + "createrole" = true + "ensure" = "present" + "inherit" = true + "login" = true + "name" = secret.data.username + "passwordSecret" = { + "name" = secret.metadata[0].name + } + "replication" = false + "superuser" = false + }] +} diff --git a/modules/ferretdb/namespace.tf b/modules/ferretdb/namespace.tf new file mode 100644 index 0000000..3e18dae --- /dev/null +++ b/modules/ferretdb/namespace.tf @@ -0,0 +1,10 @@ +// Namespace configuration for PostgreSQL Database +resource "kubernetes_namespace" "namespace" { + metadata { + name = var.namespace + labels = { + app = var.app_name + component = "namespace" + } + } +} diff --git a/modules/ferretdb/objectstore.tf b/modules/ferretdb/objectstore.tf new file mode 100644 index 0000000..cebeedc --- /dev/null +++ b/modules/ferretdb/objectstore.tf @@ -0,0 +1,48 @@ +resource "kubernetes_manifest" "barman_object_store" { + manifest = { + "apiVersion" = "barmancloud.cnpg.io/v1" + "kind" = "ObjectStore" + "metadata" = { + "labels" = { + "app" = var.app_name + "component" = "objectstore" + } + "name" = "${var.cluster_name}-objectstore" + "namespace" = kubernetes_namespace.namespace.metadata[0].name + } + "spec" = { + "configuration" = { + "data" = { + "additionalCommandArgs" = [ + "--min-chunk-size=5MB", + "--read-timeout=60", + "-vv", + ] + } + "destinationPath" = "s3://${var.backup_bucket_name}/" + "endpointCA" = { + "key" = "ca.crt" + "name" = kubernetes_secret.garage_certificate_authority.metadata[0].name + } + "endpointURL" = "https://garage-service.${var.garage_namespace}.svc.cluster.local:3940" + "s3Credentials" = { + "accessKeyId" = { + "key" = "ACCESS_KEY_ID" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + "secretAccessKey" = { + "key" = "SECRET_ACCESS_KEY" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + "region" = { + "key" = "S3_REGION" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + } + "wal" = { + "compression" = "gzip" + } + } + } + } +} diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf new file mode 100644 index 0000000..49c4801 --- /dev/null +++ b/modules/ferretdb/outputs.tf @@ -0,0 +1,17 @@ +output "namespace" { + description = "Namespace where the PostgreSQL Database is deployed in" + value = kubernetes_namespace.namespace.metadata[0].name + depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] +} + +output "server-certificate-authority" { + description = "Certificate Authority being used with PostgreSQL Database" + value = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName + depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] +} + +output "cluster_name" { + description = "Name of the CNPG PostgreSQL Cluster" + value = var.cluster_name + depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] +} diff --git a/modules/ferretdb/poddisruptionbudget.tf b/modules/ferretdb/poddisruptionbudget.tf new file mode 100644 index 0000000..1f8cfd4 --- /dev/null +++ b/modules/ferretdb/poddisruptionbudget.tf @@ -0,0 +1,21 @@ +resource "kubernetes_pod_disruption_budget_v1" "cnpg_pdb" { + metadata { + name = "cnpg-cluster-pdb" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "pdb" + } + } + + spec { + min_available = 1 + selector { + match_labels = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + + depends_on = [kubernetes_manifest.cluster] +} From c2c5a6e5031fc5cd28542f817798017fef005c9e Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:55:52 +0530 Subject: [PATCH 04/39] feat(example): deploy cnpg cluster for ferretdb --- example/main.tf | 35 ++++++++++++++++++++++++++++++++++- example/variables.tf | 8 +++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/example/main.tf b/example/main.tf index 1f3a883..910120f 100644 --- a/example/main.tf +++ b/example/main.tf @@ -31,7 +31,7 @@ module "garage" { domain = var.domain // Granting required namespaces access to the Garage cluster - access_namespaces = "postgres" + access_namespaces = "postgres,ferret" // Configuring required configurations on the Garage Cluster required_buckets = var.garage_required_buckets @@ -79,6 +79,39 @@ module "cnpg" { depends_on = [module.garage] } +# FerretDB Deployment for MongoDB Database Solution +module "ferretdb" { + source = "git::https://github.com/necro-cloud/modules//modules/ferretdb?ref=task/65/ferretdb-setup" + + // Garage Cluster Details for configuration of PITR Backups + garage_certificate_authority = module.garage.garage_internal_certificate_secret + garage_namespace = module.garage.garage_namespace + garage_configuration = "walbackups-credentials" + backup_bucket_name = "ferret" + + // Required client details to allow access and generate credentials and certificates for + clients = [ + { + namespace = "cloud" + user = "cloud" + database = "cloud" + derRequired = false + privateKeyEncoding = "PKCS1" + } + ] + + // Certificate details for internal certificates + cluster_issuer_name = module.cluster-issuer.cluster-issuer-name + + // Whitelisting Kubernetes API Endpoints in the Network Policy + kubernetes_api_ip = one(flatten(data.kubernetes_endpoints_v1.kubernetes_api_endpoint.subset[*].address[*].ip)) + kubernetes_api_protocol = one(flatten(data.kubernetes_endpoints_v1.kubernetes_api_endpoint.subset[*].port[*].protocol)) + kubernetes_api_port = one(flatten(data.kubernetes_endpoints_v1.kubernetes_api_endpoint.subset[*].port[*].port)) + + // Dependency on Garage Deployment + depends_on = [module.garage] +} + # Keycloak Cluster Deployment for Identity Solution module "keycloak" { source = "git::https://github.com/necro-cloud/modules//modules/keycloak?ref=main" diff --git a/example/variables.tf b/example/variables.tf index f969200..6fb7db4 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -41,6 +41,12 @@ variable "garage_required_access_keys" { owner = true, read = true, write = true + }, + { + bucket = "ferret", + owner = true, + read = true, + write = true } ] }, @@ -78,7 +84,7 @@ variable "garage_required_access_keys" { variable "garage_required_buckets" { description = "Buckets to deploy in the Garage Cluster" type = list(string) - default = ["cloud", "postgresql"] + default = ["cloud", "postgresql", "ferret"] } # --------------- KEYCLOAK REALM CONFIGURATION VARIABLES --------------- # From eecab61fa7bee2d25222993d594e97aec8bc1b17 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:58:34 +0530 Subject: [PATCH 05/39] fix(ferretdb): variable reference fixes --- modules/ferretdb/outputs.tf | 14 +------------- modules/ferretdb/secrets.tf | 2 +- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf index 49c4801..29cef6b 100644 --- a/modules/ferretdb/outputs.tf +++ b/modules/ferretdb/outputs.tf @@ -1,17 +1,5 @@ output "namespace" { description = "Namespace where the PostgreSQL Database is deployed in" value = kubernetes_namespace.namespace.metadata[0].name - depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] -} - -output "server-certificate-authority" { - description = "Certificate Authority being used with PostgreSQL Database" - value = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName - depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] -} - -output "cluster_name" { - description = "Name of the CNPG PostgreSQL Cluster" - value = var.cluster_name - depends_on = [kubernetes_manifest.databases, kubernetes_manifest.keycloak_database] + depends_on = [ubernetes_manifest.ferret_database] } diff --git a/modules/ferretdb/secrets.tf b/modules/ferretdb/secrets.tf index 815bb83..cdced90 100644 --- a/modules/ferretdb/secrets.tf +++ b/modules/ferretdb/secrets.tf @@ -49,7 +49,7 @@ resource "kubernetes_secret" "ferret_database_credentials" { data = { "username" = "ferret" - "password" = random_password.keycloak_password.result + "password" = random_password.ferret_password.result } type = "kubernetes.io/basic-auth" From 51ef982452aa78bef3d630733c81b1acbd787f66 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 11:59:34 +0530 Subject: [PATCH 06/39] fix(ferretdb): variable reference fixes --- modules/ferretdb/outputs.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf index 29cef6b..3d7975d 100644 --- a/modules/ferretdb/outputs.tf +++ b/modules/ferretdb/outputs.tf @@ -1,5 +1,5 @@ output "namespace" { description = "Namespace where the PostgreSQL Database is deployed in" value = kubernetes_namespace.namespace.metadata[0].name - depends_on = [ubernetes_manifest.ferret_database] + depends_on = [kubernetes_manifest.ferret_database] } From aaa587a1a2854f93a21498b6b7046af4679acc1a Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:03:51 +0530 Subject: [PATCH 07/39] fix(ferretdb): database extensions fixes --- modules/ferretdb/database.tf | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 21c43f7..17d1a31 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -19,14 +19,16 @@ resource "kubernetes_manifest" "ferret_database" { "cluster" = { "name" = kubernetes_manifest.cluster.manifest.metadata.name } - "extensions" = { - "vector" = { + "extensions" = [ + { + "name" = "vector" "ensure" = "present" - } - "pg_documentdb" = { + }, + { + "name" = "pg_documentdb" "ensure" = "present" } - } + ] } } From 00e8988c429d75cc2fb716809b689ea997555e35 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:08:21 +0530 Subject: [PATCH 08/39] fix(ferretdb): database shared preload libraries update --- modules/ferretdb/cluster.tf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 0c5540e..894280f 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -42,8 +42,11 @@ resource "kubernetes_manifest" "cluster" { "instances" = var.cluster_size // Required postgresql configuration for DocumentDB "postgresql" = { + "shared_preload_libraries" = [ + "pg_documentdb_core", + "pg_documentdb" + ] "parameters" = { - "shared_preload_libraries" = "pg_documentdb_core,pg_documentdb" "search_path" = "\"$user\", public, documentdb_api, documentdb_core" } } From 5e66ad6f8aa11ee89f52d9f42b15812beeedbcc5 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:15:55 +0530 Subject: [PATCH 09/39] fix(ferretdb): trying to recreate the cluster --- modules/ferretdb/cluster.tf | 260 ++++++++++++++++---------------- modules/ferretdb/objectstore.tf | 96 ++++++------ 2 files changed, 178 insertions(+), 178 deletions(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 894280f..88bc9a6 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -1,133 +1,133 @@ -// CloudNative PG Cluster -resource "kubernetes_manifest" "cluster" { - manifest = { - "apiVersion" = "postgresql.cnpg.io/v1" - "kind" = "Cluster" - "metadata" = { - "labels" = { - "app" = var.app_name - "component" = "cluster" - } - "name" = var.cluster_name - "namespace" = kubernetes_namespace.namespace.metadata[0].name - } - "spec" = { - "inheritedMetadata" = { - "labels" = { - "garage-access" = true - } - } - "postgresUID" = 999 - "postgresGID" = 999 - "topologySpreadConstraints" = [ - { - "maxSkew" = 1 - "topologyKey" = "kubernetes.io/hostname" - "whenUnsatisfiable" = "DoNotSchedule" - "labelSelector" = { - "matchLabels" = { - "cnpg.io/cluster" = var.cluster_name - } - } - } - ] - "imageCatalogRef" = { - "apiGroup" = "postgresql.cnpg.io", - "kind" = "ClusterImageCatalog" - "name" = kubernetes_manifest.ferret_cluster_image_catalog.manifest.metadata.name - "major" = var.cluster_postgresql_version - } - "description" = "PostgreSQL Cluster for storing relational data" - "enableSuperuserAccess" = true - "instances" = var.cluster_size - // Required postgresql configuration for DocumentDB - "postgresql" = { - "shared_preload_libraries" = [ - "pg_documentdb_core", - "pg_documentdb" - ] - "parameters" = { - "search_path" = "\"$user\", public, documentdb_api, documentdb_core" - } - } - "managed" = { - "roles" = concat([ - { - "bypassrls" = false - "comment" = "ferret user for postgresql" - "connectionLimit" = -1 - "createdb" = true - "createrole" = true - "ensure" = "present" - "inherit" = true - "login" = true - "name" = "ferret" - "passwordSecret" = { - "name" = kubernetes_secret.ferret_database_credentials.metadata[0].name - } - "replication" = false - "superuser" = false - }, - ], local.managed_roles) - } - "primaryUpdateStrategy" = "unsupervised" - "resources" = { - "limits" = { - "cpu" = "500m" - "memory" = "1Gi" - } - "requests" = { - "cpu" = "500m" - "memory" = "1Gi" - } - } - "startDelay" = 300 - "storage" = { - "pvcTemplate" = { - "accessModes" = [ - "ReadWriteOnce", - ] - "resources" = { - "requests" = { - "storage" = "5Gi" - } - } - "storageClassName" = "local-path" - "volumeMode" = "Filesystem" - } - "size" = "5Gi" - } - "certificates" = { - "serverTLSSecret" = kubernetes_manifest.server_certificate.manifest.spec.secretName - "serverCASecret" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName - "clientCASecret" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName - "replicationTLSSecret" = kubernetes_manifest.client_streaming_replica_certificate.manifest.spec.secretName - } - "plugins" = [ - { - "name" = "barman-cloud.cloudnative-pg.io" - "isWALArchiver" = true - "parameters" = { - "barmanObjectName" = kubernetes_manifest.barman_object_store.manifest.metadata.name - } - } - ] - } - } +# // CloudNative PG Cluster +# resource "kubernetes_manifest" "cluster" { +# manifest = { +# "apiVersion" = "postgresql.cnpg.io/v1" +# "kind" = "Cluster" +# "metadata" = { +# "labels" = { +# "app" = var.app_name +# "component" = "cluster" +# } +# "name" = var.cluster_name +# "namespace" = kubernetes_namespace.namespace.metadata[0].name +# } +# "spec" = { +# "inheritedMetadata" = { +# "labels" = { +# "garage-access" = true +# } +# } +# "postgresUID" = 999 +# "postgresGID" = 999 +# "topologySpreadConstraints" = [ +# { +# "maxSkew" = 1 +# "topologyKey" = "kubernetes.io/hostname" +# "whenUnsatisfiable" = "DoNotSchedule" +# "labelSelector" = { +# "matchLabels" = { +# "cnpg.io/cluster" = var.cluster_name +# } +# } +# } +# ] +# "imageCatalogRef" = { +# "apiGroup" = "postgresql.cnpg.io", +# "kind" = "ClusterImageCatalog" +# "name" = kubernetes_manifest.ferret_cluster_image_catalog.manifest.metadata.name +# "major" = var.cluster_postgresql_version +# } +# "description" = "PostgreSQL Cluster for storing relational data" +# "enableSuperuserAccess" = true +# "instances" = var.cluster_size +# // Required postgresql configuration for DocumentDB +# "postgresql" = { +# "shared_preload_libraries" = [ +# "pg_documentdb_core", +# "pg_documentdb" +# ] +# "parameters" = { +# "search_path" = "\"$user\", public, documentdb_api, documentdb_core" +# } +# } +# "managed" = { +# "roles" = concat([ +# { +# "bypassrls" = false +# "comment" = "ferret user for postgresql" +# "connectionLimit" = -1 +# "createdb" = true +# "createrole" = true +# "ensure" = "present" +# "inherit" = true +# "login" = true +# "name" = "ferret" +# "passwordSecret" = { +# "name" = kubernetes_secret.ferret_database_credentials.metadata[0].name +# } +# "replication" = false +# "superuser" = false +# }, +# ], local.managed_roles) +# } +# "primaryUpdateStrategy" = "unsupervised" +# "resources" = { +# "limits" = { +# "cpu" = "500m" +# "memory" = "1Gi" +# } +# "requests" = { +# "cpu" = "500m" +# "memory" = "1Gi" +# } +# } +# "startDelay" = 300 +# "storage" = { +# "pvcTemplate" = { +# "accessModes" = [ +# "ReadWriteOnce", +# ] +# "resources" = { +# "requests" = { +# "storage" = "5Gi" +# } +# } +# "storageClassName" = "local-path" +# "volumeMode" = "Filesystem" +# } +# "size" = "5Gi" +# } +# "certificates" = { +# "serverTLSSecret" = kubernetes_manifest.server_certificate.manifest.spec.secretName +# "serverCASecret" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName +# "clientCASecret" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName +# "replicationTLSSecret" = kubernetes_manifest.client_streaming_replica_certificate.manifest.spec.secretName +# } +# "plugins" = [ +# { +# "name" = "barman-cloud.cloudnative-pg.io" +# "isWALArchiver" = true +# "parameters" = { +# "barmanObjectName" = kubernetes_manifest.barman_object_store.manifest.metadata.name +# } +# } +# ] +# } +# } - // Fields to ignore changes for - computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] +# // Fields to ignore changes for +# computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] - wait { - condition { - type = "Ready" - status = "True" - } - } +# wait { +# condition { +# type = "Ready" +# status = "True" +# } +# } - timeouts { - create = "10m" - update = "10m" - delete = "10m" - } -} +# timeouts { +# create = "10m" +# update = "10m" +# delete = "10m" +# } +# } diff --git a/modules/ferretdb/objectstore.tf b/modules/ferretdb/objectstore.tf index cebeedc..5274a75 100644 --- a/modules/ferretdb/objectstore.tf +++ b/modules/ferretdb/objectstore.tf @@ -1,48 +1,48 @@ -resource "kubernetes_manifest" "barman_object_store" { - manifest = { - "apiVersion" = "barmancloud.cnpg.io/v1" - "kind" = "ObjectStore" - "metadata" = { - "labels" = { - "app" = var.app_name - "component" = "objectstore" - } - "name" = "${var.cluster_name}-objectstore" - "namespace" = kubernetes_namespace.namespace.metadata[0].name - } - "spec" = { - "configuration" = { - "data" = { - "additionalCommandArgs" = [ - "--min-chunk-size=5MB", - "--read-timeout=60", - "-vv", - ] - } - "destinationPath" = "s3://${var.backup_bucket_name}/" - "endpointCA" = { - "key" = "ca.crt" - "name" = kubernetes_secret.garage_certificate_authority.metadata[0].name - } - "endpointURL" = "https://garage-service.${var.garage_namespace}.svc.cluster.local:3940" - "s3Credentials" = { - "accessKeyId" = { - "key" = "ACCESS_KEY_ID" - "name" = kubernetes_secret.garage_configuration.metadata[0].name - } - "secretAccessKey" = { - "key" = "SECRET_ACCESS_KEY" - "name" = kubernetes_secret.garage_configuration.metadata[0].name - } - "region" = { - "key" = "S3_REGION" - "name" = kubernetes_secret.garage_configuration.metadata[0].name - } - } - "wal" = { - "compression" = "gzip" - } - } - } - } -} +# resource "kubernetes_manifest" "barman_object_store" { +# manifest = { +# "apiVersion" = "barmancloud.cnpg.io/v1" +# "kind" = "ObjectStore" +# "metadata" = { +# "labels" = { +# "app" = var.app_name +# "component" = "objectstore" +# } +# "name" = "${var.cluster_name}-objectstore" +# "namespace" = kubernetes_namespace.namespace.metadata[0].name +# } +# "spec" = { +# "configuration" = { +# "data" = { +# "additionalCommandArgs" = [ +# "--min-chunk-size=5MB", +# "--read-timeout=60", +# "-vv", +# ] +# } +# "destinationPath" = "s3://${var.backup_bucket_name}/" +# "endpointCA" = { +# "key" = "ca.crt" +# "name" = kubernetes_secret.garage_certificate_authority.metadata[0].name +# } +# "endpointURL" = "https://garage-service.${var.garage_namespace}.svc.cluster.local:3940" +# "s3Credentials" = { +# "accessKeyId" = { +# "key" = "ACCESS_KEY_ID" +# "name" = kubernetes_secret.garage_configuration.metadata[0].name +# } +# "secretAccessKey" = { +# "key" = "SECRET_ACCESS_KEY" +# "name" = kubernetes_secret.garage_configuration.metadata[0].name +# } +# "region" = { +# "key" = "S3_REGION" +# "name" = kubernetes_secret.garage_configuration.metadata[0].name +# } +# } +# "wal" = { +# "compression" = "gzip" +# } +# } +# } +# } +# } From 8c981e38e319d3b753326be927a55f63371f7426 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:16:49 +0530 Subject: [PATCH 10/39] fix(ferretdb): recreate the cluster --- modules/ferretdb/cluster.tf | 260 ++++++++++++++++++------------------ 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 88bc9a6..894280f 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -1,133 +1,133 @@ -# // CloudNative PG Cluster -# resource "kubernetes_manifest" "cluster" { -# manifest = { -# "apiVersion" = "postgresql.cnpg.io/v1" -# "kind" = "Cluster" -# "metadata" = { -# "labels" = { -# "app" = var.app_name -# "component" = "cluster" -# } -# "name" = var.cluster_name -# "namespace" = kubernetes_namespace.namespace.metadata[0].name -# } -# "spec" = { -# "inheritedMetadata" = { -# "labels" = { -# "garage-access" = true -# } -# } -# "postgresUID" = 999 -# "postgresGID" = 999 -# "topologySpreadConstraints" = [ -# { -# "maxSkew" = 1 -# "topologyKey" = "kubernetes.io/hostname" -# "whenUnsatisfiable" = "DoNotSchedule" -# "labelSelector" = { -# "matchLabels" = { -# "cnpg.io/cluster" = var.cluster_name -# } -# } -# } -# ] -# "imageCatalogRef" = { -# "apiGroup" = "postgresql.cnpg.io", -# "kind" = "ClusterImageCatalog" -# "name" = kubernetes_manifest.ferret_cluster_image_catalog.manifest.metadata.name -# "major" = var.cluster_postgresql_version -# } -# "description" = "PostgreSQL Cluster for storing relational data" -# "enableSuperuserAccess" = true -# "instances" = var.cluster_size -# // Required postgresql configuration for DocumentDB -# "postgresql" = { -# "shared_preload_libraries" = [ -# "pg_documentdb_core", -# "pg_documentdb" -# ] -# "parameters" = { -# "search_path" = "\"$user\", public, documentdb_api, documentdb_core" -# } -# } -# "managed" = { -# "roles" = concat([ -# { -# "bypassrls" = false -# "comment" = "ferret user for postgresql" -# "connectionLimit" = -1 -# "createdb" = true -# "createrole" = true -# "ensure" = "present" -# "inherit" = true -# "login" = true -# "name" = "ferret" -# "passwordSecret" = { -# "name" = kubernetes_secret.ferret_database_credentials.metadata[0].name -# } -# "replication" = false -# "superuser" = false -# }, -# ], local.managed_roles) -# } -# "primaryUpdateStrategy" = "unsupervised" -# "resources" = { -# "limits" = { -# "cpu" = "500m" -# "memory" = "1Gi" -# } -# "requests" = { -# "cpu" = "500m" -# "memory" = "1Gi" -# } -# } -# "startDelay" = 300 -# "storage" = { -# "pvcTemplate" = { -# "accessModes" = [ -# "ReadWriteOnce", -# ] -# "resources" = { -# "requests" = { -# "storage" = "5Gi" -# } -# } -# "storageClassName" = "local-path" -# "volumeMode" = "Filesystem" -# } -# "size" = "5Gi" -# } -# "certificates" = { -# "serverTLSSecret" = kubernetes_manifest.server_certificate.manifest.spec.secretName -# "serverCASecret" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName -# "clientCASecret" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName -# "replicationTLSSecret" = kubernetes_manifest.client_streaming_replica_certificate.manifest.spec.secretName -# } -# "plugins" = [ -# { -# "name" = "barman-cloud.cloudnative-pg.io" -# "isWALArchiver" = true -# "parameters" = { -# "barmanObjectName" = kubernetes_manifest.barman_object_store.manifest.metadata.name -# } -# } -# ] -# } -# } +// CloudNative PG Cluster +resource "kubernetes_manifest" "cluster" { + manifest = { + "apiVersion" = "postgresql.cnpg.io/v1" + "kind" = "Cluster" + "metadata" = { + "labels" = { + "app" = var.app_name + "component" = "cluster" + } + "name" = var.cluster_name + "namespace" = kubernetes_namespace.namespace.metadata[0].name + } + "spec" = { + "inheritedMetadata" = { + "labels" = { + "garage-access" = true + } + } + "postgresUID" = 999 + "postgresGID" = 999 + "topologySpreadConstraints" = [ + { + "maxSkew" = 1 + "topologyKey" = "kubernetes.io/hostname" + "whenUnsatisfiable" = "DoNotSchedule" + "labelSelector" = { + "matchLabels" = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + ] + "imageCatalogRef" = { + "apiGroup" = "postgresql.cnpg.io", + "kind" = "ClusterImageCatalog" + "name" = kubernetes_manifest.ferret_cluster_image_catalog.manifest.metadata.name + "major" = var.cluster_postgresql_version + } + "description" = "PostgreSQL Cluster for storing relational data" + "enableSuperuserAccess" = true + "instances" = var.cluster_size + // Required postgresql configuration for DocumentDB + "postgresql" = { + "shared_preload_libraries" = [ + "pg_documentdb_core", + "pg_documentdb" + ] + "parameters" = { + "search_path" = "\"$user\", public, documentdb_api, documentdb_core" + } + } + "managed" = { + "roles" = concat([ + { + "bypassrls" = false + "comment" = "ferret user for postgresql" + "connectionLimit" = -1 + "createdb" = true + "createrole" = true + "ensure" = "present" + "inherit" = true + "login" = true + "name" = "ferret" + "passwordSecret" = { + "name" = kubernetes_secret.ferret_database_credentials.metadata[0].name + } + "replication" = false + "superuser" = false + }, + ], local.managed_roles) + } + "primaryUpdateStrategy" = "unsupervised" + "resources" = { + "limits" = { + "cpu" = "500m" + "memory" = "1Gi" + } + "requests" = { + "cpu" = "500m" + "memory" = "1Gi" + } + } + "startDelay" = 300 + "storage" = { + "pvcTemplate" = { + "accessModes" = [ + "ReadWriteOnce", + ] + "resources" = { + "requests" = { + "storage" = "5Gi" + } + } + "storageClassName" = "local-path" + "volumeMode" = "Filesystem" + } + "size" = "5Gi" + } + "certificates" = { + "serverTLSSecret" = kubernetes_manifest.server_certificate.manifest.spec.secretName + "serverCASecret" = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName + "clientCASecret" = kubernetes_manifest.client_certificate_authority.manifest.spec.secretName + "replicationTLSSecret" = kubernetes_manifest.client_streaming_replica_certificate.manifest.spec.secretName + } + "plugins" = [ + { + "name" = "barman-cloud.cloudnative-pg.io" + "isWALArchiver" = true + "parameters" = { + "barmanObjectName" = kubernetes_manifest.barman_object_store.manifest.metadata.name + } + } + ] + } + } -# // Fields to ignore changes for -# computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] + // Fields to ignore changes for + computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] -# wait { -# condition { -# type = "Ready" -# status = "True" -# } -# } + wait { + condition { + type = "Ready" + status = "True" + } + } -# timeouts { -# create = "10m" -# update = "10m" -# delete = "10m" -# } -# } + timeouts { + create = "10m" + update = "10m" + delete = "10m" + } +} From c971d4ec3b41aa37b1fe3962a4c844b3bd09c5ae Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:20:10 +0530 Subject: [PATCH 11/39] fix(ferretdb): recreate the cluster --- modules/ferretdb/objectstore.tf | 96 ++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/modules/ferretdb/objectstore.tf b/modules/ferretdb/objectstore.tf index 5274a75..cebeedc 100644 --- a/modules/ferretdb/objectstore.tf +++ b/modules/ferretdb/objectstore.tf @@ -1,48 +1,48 @@ -# resource "kubernetes_manifest" "barman_object_store" { -# manifest = { -# "apiVersion" = "barmancloud.cnpg.io/v1" -# "kind" = "ObjectStore" -# "metadata" = { -# "labels" = { -# "app" = var.app_name -# "component" = "objectstore" -# } -# "name" = "${var.cluster_name}-objectstore" -# "namespace" = kubernetes_namespace.namespace.metadata[0].name -# } -# "spec" = { -# "configuration" = { -# "data" = { -# "additionalCommandArgs" = [ -# "--min-chunk-size=5MB", -# "--read-timeout=60", -# "-vv", -# ] -# } -# "destinationPath" = "s3://${var.backup_bucket_name}/" -# "endpointCA" = { -# "key" = "ca.crt" -# "name" = kubernetes_secret.garage_certificate_authority.metadata[0].name -# } -# "endpointURL" = "https://garage-service.${var.garage_namespace}.svc.cluster.local:3940" -# "s3Credentials" = { -# "accessKeyId" = { -# "key" = "ACCESS_KEY_ID" -# "name" = kubernetes_secret.garage_configuration.metadata[0].name -# } -# "secretAccessKey" = { -# "key" = "SECRET_ACCESS_KEY" -# "name" = kubernetes_secret.garage_configuration.metadata[0].name -# } -# "region" = { -# "key" = "S3_REGION" -# "name" = kubernetes_secret.garage_configuration.metadata[0].name -# } -# } -# "wal" = { -# "compression" = "gzip" -# } -# } -# } -# } -# } +resource "kubernetes_manifest" "barman_object_store" { + manifest = { + "apiVersion" = "barmancloud.cnpg.io/v1" + "kind" = "ObjectStore" + "metadata" = { + "labels" = { + "app" = var.app_name + "component" = "objectstore" + } + "name" = "${var.cluster_name}-objectstore" + "namespace" = kubernetes_namespace.namespace.metadata[0].name + } + "spec" = { + "configuration" = { + "data" = { + "additionalCommandArgs" = [ + "--min-chunk-size=5MB", + "--read-timeout=60", + "-vv", + ] + } + "destinationPath" = "s3://${var.backup_bucket_name}/" + "endpointCA" = { + "key" = "ca.crt" + "name" = kubernetes_secret.garage_certificate_authority.metadata[0].name + } + "endpointURL" = "https://garage-service.${var.garage_namespace}.svc.cluster.local:3940" + "s3Credentials" = { + "accessKeyId" = { + "key" = "ACCESS_KEY_ID" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + "secretAccessKey" = { + "key" = "SECRET_ACCESS_KEY" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + "region" = { + "key" = "S3_REGION" + "name" = kubernetes_secret.garage_configuration.metadata[0].name + } + } + "wal" = { + "compression" = "gzip" + } + } + } + } +} From a0d3ef99f9535f197c09cd7961ccc87073f68ef3 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:26:19 +0530 Subject: [PATCH 12/39] fix(ferretdb): computed fields for parameters --- modules/ferretdb/cluster.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 894280f..0a77a87 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -116,7 +116,7 @@ resource "kubernetes_manifest" "cluster" { } // Fields to ignore changes for - computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication"] + computed_fields = ["spec.managed.roles[0].replication", "spec.managed.roles[0].superuser", "spec.managed.roles[0].bypassrls", "spec.managed.roles[1].bypassrls", "spec.managed.roles[1].superuser", "spec.managed.roles[1].replication", "spec.postgresql.parameters"] wait { condition { From e87e55d251eb5073fa2f6d2b4081d0ba416b6530 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:33:28 +0530 Subject: [PATCH 13/39] fix(ferretdb): extensions fix for the database --- modules/ferretdb/database.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 17d1a31..953e24b 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -25,7 +25,7 @@ resource "kubernetes_manifest" "ferret_database" { "ensure" = "present" }, { - "name" = "pg_documentdb" + "name" = "documentdb" "ensure" = "present" } ] From 65bcab7cfa22ec8f9494633887e5e1a150543f90 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:35:28 +0530 Subject: [PATCH 14/39] fix(ferretdb): core extension fix for the database --- modules/ferretdb/database.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 953e24b..4175751 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -24,6 +24,10 @@ resource "kubernetes_manifest" "ferret_database" { "name" = "vector" "ensure" = "present" }, + { + "name" = "documentdb_core" + "ensure" = "present" + }, { "name" = "documentdb" "ensure" = "present" From 4300054920f17e5c723c79f1a97a72da65d3e903 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 12:40:12 +0530 Subject: [PATCH 15/39] fix(ferretdb): pg_cron extension fix for the database --- modules/ferretdb/cluster.tf | 4 +++- modules/ferretdb/database.tf | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 0a77a87..4c953cd 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -43,11 +43,13 @@ resource "kubernetes_manifest" "cluster" { // Required postgresql configuration for DocumentDB "postgresql" = { "shared_preload_libraries" = [ + "pg_cron", "pg_documentdb_core", - "pg_documentdb" + "pg_documentdb", ] "parameters" = { "search_path" = "\"$user\", public, documentdb_api, documentdb_core" + "cron.database_name" = "ferret" } } "managed" = { diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 4175751..9a5c468 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -24,6 +24,10 @@ resource "kubernetes_manifest" "ferret_database" { "name" = "vector" "ensure" = "present" }, + { + "name" = "pg_cron" + "ensure" = "present" + }, { "name" = "documentdb_core" "ensure" = "present" From 223b91ee2a41cb12cf0e5e09c0a31eab10d5082d Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 13:41:22 +0530 Subject: [PATCH 16/39] fix(ferretdb): tsm_system_rows extension fix for the database --- modules/ferretdb/database.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 9a5c468..3d40b16 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -24,6 +24,10 @@ resource "kubernetes_manifest" "ferret_database" { "name" = "vector" "ensure" = "present" }, + { + "name" = "tsm_system_rows" + "ensure" = "present" + }, { "name" = "pg_cron" "ensure" = "present" From 71c1806b5d5afa73122417cd7cebb7c145fa523e Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 13:47:26 +0530 Subject: [PATCH 17/39] fix(ferretdb): postgis extension fix for the database --- modules/ferretdb/database.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 3d40b16..1fbdf02 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -28,6 +28,10 @@ resource "kubernetes_manifest" "ferret_database" { "name" = "tsm_system_rows" "ensure" = "present" }, + { + "name" = "postgis" + "ensure" = "present" + }, { "name" = "pg_cron" "ensure" = "present" From 715db6e2b48754a6ee6aeb635bfb2e6eb37e4ace Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 13:52:09 +0530 Subject: [PATCH 18/39] fix(ferretdb): move database to postInitSQL script --- modules/ferretdb/cluster.tf | 10 +++ modules/ferretdb/database.tf | 118 +++++++++++++++++------------------ 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 4c953cd..3ffac19 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -51,6 +51,16 @@ resource "kubernetes_manifest" "cluster" { "search_path" = "\"$user\", public, documentdb_api, documentdb_core" "cron.database_name" = "ferret" } + }, + "bootstrap" = { + "initdb" = { + "database" = "ferret" + "owner" = "ferret" + "postInitSQL" = [ + "CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;", + "ALTER ROLE ferret SET search_path TO \"$user\", public, documentdb_api, documentdb_core;" + ] + } } "managed" = { "roles" = concat([ diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index 1fbdf02..e224540 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -1,62 +1,62 @@ -// Database Configuration for Ferret -resource "kubernetes_manifest" "ferret_database" { - manifest = { - "apiVersion" : "postgresql.cnpg.io/v1" - "kind" : "Database" - "metadata" = { - "name" = "ferret" - "namespace" = kubernetes_namespace.namespace.metadata[0].name - "labels" = { - "app" = var.app_name - "component" = "cluster" - } - } +# // Database Configuration for Ferret +# resource "kubernetes_manifest" "ferret_database" { +# manifest = { +# "apiVersion" : "postgresql.cnpg.io/v1" +# "kind" : "Database" +# "metadata" = { +# "name" = "ferret" +# "namespace" = kubernetes_namespace.namespace.metadata[0].name +# "labels" = { +# "app" = var.app_name +# "component" = "cluster" +# } +# } - "spec" = { - "databaseReclaimPolicy" : "delete" - "name" : "ferret" - "owner" : "ferret" - "cluster" = { - "name" = kubernetes_manifest.cluster.manifest.metadata.name - } - "extensions" = [ - { - "name" = "vector" - "ensure" = "present" - }, - { - "name" = "tsm_system_rows" - "ensure" = "present" - }, - { - "name" = "postgis" - "ensure" = "present" - }, - { - "name" = "pg_cron" - "ensure" = "present" - }, - { - "name" = "documentdb_core" - "ensure" = "present" - }, - { - "name" = "documentdb" - "ensure" = "present" - } - ] - } - } +# "spec" = { +# "databaseReclaimPolicy" : "delete" +# "name" : "ferret" +# "owner" : "ferret" +# "cluster" = { +# "name" = kubernetes_manifest.cluster.manifest.metadata.name +# } +# "extensions" = [ +# { +# "name" = "vector" +# "ensure" = "present" +# }, +# { +# "name" = "tsm_system_rows" +# "ensure" = "present" +# }, +# { +# "name" = "postgis" +# "ensure" = "present" +# }, +# { +# "name" = "pg_cron" +# "ensure" = "present" +# }, +# { +# "name" = "documentdb_core" +# "ensure" = "present" +# }, +# { +# "name" = "documentdb" +# "ensure" = "present" +# } +# ] +# } +# } - wait { - fields = { - "status.applied" = "true" - } - } +# wait { +# fields = { +# "status.applied" = "true" +# } +# } - timeouts { - create = "5m" - update = "5m" - delete = "5m" - } -} +# timeouts { +# create = "5m" +# update = "5m" +# delete = "5m" +# } +# } From 37d8f01abd8a838e0649163119e6a43744a82a7e Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 13:53:21 +0530 Subject: [PATCH 19/39] fix(ferretdb): dependency fixes --- modules/ferretdb/outputs.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf index 3d7975d..872bee4 100644 --- a/modules/ferretdb/outputs.tf +++ b/modules/ferretdb/outputs.tf @@ -1,5 +1,5 @@ output "namespace" { description = "Namespace where the PostgreSQL Database is deployed in" value = kubernetes_namespace.namespace.metadata[0].name - depends_on = [kubernetes_manifest.ferret_database] + # depends_on = [kubernetes_manifest.ferret_database] } From 9714f95946846b2884ae0cfd478e4d2a4b9d3a3a Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 13:57:15 +0530 Subject: [PATCH 20/39] fix(ferretdb): rum extension fix for the database --- modules/ferretdb/cluster.tf | 10 --- modules/ferretdb/database.tf | 122 ++++++++++++++++++----------------- modules/ferretdb/outputs.tf | 2 +- 3 files changed, 64 insertions(+), 70 deletions(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 3ffac19..4c953cd 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -51,16 +51,6 @@ resource "kubernetes_manifest" "cluster" { "search_path" = "\"$user\", public, documentdb_api, documentdb_core" "cron.database_name" = "ferret" } - }, - "bootstrap" = { - "initdb" = { - "database" = "ferret" - "owner" = "ferret" - "postInitSQL" = [ - "CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;", - "ALTER ROLE ferret SET search_path TO \"$user\", public, documentdb_api, documentdb_core;" - ] - } } "managed" = { "roles" = concat([ diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf index e224540..b8de56d 100644 --- a/modules/ferretdb/database.tf +++ b/modules/ferretdb/database.tf @@ -1,62 +1,66 @@ -# // Database Configuration for Ferret -# resource "kubernetes_manifest" "ferret_database" { -# manifest = { -# "apiVersion" : "postgresql.cnpg.io/v1" -# "kind" : "Database" -# "metadata" = { -# "name" = "ferret" -# "namespace" = kubernetes_namespace.namespace.metadata[0].name -# "labels" = { -# "app" = var.app_name -# "component" = "cluster" -# } -# } +// Database Configuration for Ferret +resource "kubernetes_manifest" "ferret_database" { + manifest = { + "apiVersion" : "postgresql.cnpg.io/v1" + "kind" : "Database" + "metadata" = { + "name" = "ferret" + "namespace" = kubernetes_namespace.namespace.metadata[0].name + "labels" = { + "app" = var.app_name + "component" = "cluster" + } + } -# "spec" = { -# "databaseReclaimPolicy" : "delete" -# "name" : "ferret" -# "owner" : "ferret" -# "cluster" = { -# "name" = kubernetes_manifest.cluster.manifest.metadata.name -# } -# "extensions" = [ -# { -# "name" = "vector" -# "ensure" = "present" -# }, -# { -# "name" = "tsm_system_rows" -# "ensure" = "present" -# }, -# { -# "name" = "postgis" -# "ensure" = "present" -# }, -# { -# "name" = "pg_cron" -# "ensure" = "present" -# }, -# { -# "name" = "documentdb_core" -# "ensure" = "present" -# }, -# { -# "name" = "documentdb" -# "ensure" = "present" -# } -# ] -# } -# } + "spec" = { + "databaseReclaimPolicy" : "delete" + "name" : "ferret" + "owner" : "ferret" + "cluster" = { + "name" = kubernetes_manifest.cluster.manifest.metadata.name + } + "extensions" = [ + { + "name" = "vector" + "ensure" = "present" + }, + { + "name" = "tsm_system_rows" + "ensure" = "present" + }, + { + "name" = "postgis" + "ensure" = "present" + }, + { + "name" = "rum" + "ensure" = "present" + }, + { + "name" = "pg_cron" + "ensure" = "present" + }, + { + "name" = "documentdb_core" + "ensure" = "present" + }, + { + "name" = "documentdb" + "ensure" = "present" + } + ] + } + } -# wait { -# fields = { -# "status.applied" = "true" -# } -# } + wait { + fields = { + "status.applied" = "true" + } + } -# timeouts { -# create = "5m" -# update = "5m" -# delete = "5m" -# } -# } + timeouts { + create = "5m" + update = "5m" + delete = "5m" + } +} diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf index 872bee4..3d7975d 100644 --- a/modules/ferretdb/outputs.tf +++ b/modules/ferretdb/outputs.tf @@ -1,5 +1,5 @@ output "namespace" { description = "Namespace where the PostgreSQL Database is deployed in" value = kubernetes_namespace.namespace.metadata[0].name - # depends_on = [kubernetes_manifest.ferret_database] + depends_on = [kubernetes_manifest.ferret_database] } From 93fd19af291c74d1f5b25517105b11dcd42094b2 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 14:32:42 +0530 Subject: [PATCH 21/39] feat(ferretdb): ferretdb deployment complete (hopefully) --- modules/ferretdb/ferret.tf | 97 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 modules/ferretdb/ferret.tf diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf new file mode 100644 index 0000000..debf868 --- /dev/null +++ b/modules/ferretdb/ferret.tf @@ -0,0 +1,97 @@ +resource "kubernetes_deployment" "ferretdb" { + metadata { + name = "ferret" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "deployment" + } + } + + spec { + replicas = var.cluster_size + selector { + match_labels = { + app = var.app_name + component = "pod" + } + } + + template { + metadata { + labels = { + app = var.app_name + component = "pod" + "ferret-access" = "true" + } + } + + spec { + container { + name = "ferret" + image = "ghcr.io/ferretdb/ferretdb:2.7.0" + + port { + container_port = 27017 + name = "mongo" + } + + // PostgreSQL Certificates + volume_mount { + name = "postgres-ca" + mount_path = "/etc/certs" + read_only = true + } + + env { + name = "DB_USER" + value_from { + secret_key_ref { + name = kubernetes_secret.ferret_database_credentials.metadata[0].name + key = "username" + } + } + } + env { + name = "DB_PASS" + value_from { + secret_key_ref { + name = kubernetes_secret.ferret_database_credentials.metadata[0].name + key = "password" + } + } + } + env { + name = "DB_HOST" + value = "ferret-postgresql-cluster-rw" + } + env { + name = "FERRETDB_POSTGRESQL_URL" + value = "postgres://$(DB_USER):$(DB_PASS)@$(DB_HOST):5432/ferret?sslmode=verify-ca&sslrootcert=/etc/certs/ca.crt" + } + + readiness_probe { + tcp_socket { + port = 27017 + } + initial_delay_seconds = 30 + period_seconds = 10 + } + } + + volume { + name = "postgres-ca" + secret { + secret_name = kubernetes_manifest.server_certificate_authority.manifest.spec.secretName + items { + key = "ca.crt" + path = "ca.crt" + } + } + } + } + } + } + + depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database ] +} From 55e2e3a249c7369b1a373cb6349b2548e916d6bd Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 14:54:53 +0530 Subject: [PATCH 22/39] feat(ferretdb): ferretdb job to give access to ferret artifact --- modules/ferretdb/ferret.tf | 2 +- modules/ferretdb/job.tf | 92 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 modules/ferretdb/job.tf diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index debf868..498d351 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -93,5 +93,5 @@ resource "kubernetes_deployment" "ferretdb" { } } - depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database ] + depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database, kubernetes_job.ferret_permissions ] } diff --git a/modules/ferretdb/job.tf b/modules/ferretdb/job.tf new file mode 100644 index 0000000..ea445ea --- /dev/null +++ b/modules/ferretdb/job.tf @@ -0,0 +1,92 @@ +resource "kubernetes_job" "ferret_permissions" { + metadata { + name = "ferret-sql-permissions" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "job" + } + } + + spec { + template { + metadata { + name = "ferret-sql-permissions" + } + + spec { + restart_policy = "OnFailure" + + container { + name = "grant-permissions" + image = "postgres:17-alpine" + + env { + name = "PGHOST" + value = "ferret-postgresql-cluster-rw" + } + env { + name = "PGDATABASE" + value = "ferret" + } + env { + name = "PGUSER" + value_from { + secret_key_ref { + name = "ferret-postgresql-cluster-superuser" + key = "username" + } + } + } + env { + name = "PGPASSWORD" + value_from { + secret_key_ref { + name = "ferret-postgresql-cluster-superuser" + key = "password" + } + } + } + + command = [ + "/bin/bash", + "-c" + ] + args = [ + < Date: Mon, 12 Jan 2026 14:59:34 +0530 Subject: [PATCH 23/39] fix(ferretdb): giving sql job access to the cluster --- modules/ferretdb/job.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/ferretdb/job.tf b/modules/ferretdb/job.tf index ea445ea..ac601f8 100644 --- a/modules/ferretdb/job.tf +++ b/modules/ferretdb/job.tf @@ -12,6 +12,11 @@ resource "kubernetes_job" "ferret_permissions" { template { metadata { name = "ferret-sql-permissions" + labels = { + app = var.app_name + component = "pod" + "ferret-access" = "true" + } } spec { From 6d2744273749b10c346d8c883af47195f8f9cc60 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 15:04:15 +0530 Subject: [PATCH 24/39] fix(ferretdb): updated sql job script --- modules/ferretdb/job.tf | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ferretdb/job.tf b/modules/ferretdb/job.tf index ac601f8..1c13898 100644 --- a/modules/ferretdb/job.tf +++ b/modules/ferretdb/job.tf @@ -65,6 +65,13 @@ until pg_isready; do echo "Waiting for DB..."; sleep 2; done; # Run the Fixed Grant Script psql < Date: Mon, 12 Jan 2026 15:21:19 +0530 Subject: [PATCH 25/39] feat(ferretdb): ferret topology spread --- modules/ferretdb/ferret.tf | 60 +++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index 498d351..aa9b60b 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -23,10 +23,39 @@ resource "kubernetes_deployment" "ferretdb" { app = var.app_name component = "pod" "ferret-access" = "true" + "part-of" = "ferretdb" } } spec { + // Node Affinity rule to run only on worker nodes + affinity { + node_affinity { + required_during_scheduling_ignored_during_execution { + node_selector_term { + match_expressions { + key = "worker" + operator = "Exists" + } + } + } + } + } + + // Topology Spread to ensure pods are running on seperate nodes + topology_spread_constraint { + max_skew = 1 + topology_key = "kubernetes.io/hostname" + when_unsatisfiable = "DoNotSchedule" + label_selector { + match_labels = { + app = var.app_name + component = "pod" + "part-of" = "ferretdb" + } + } + } + container { name = "ferret" image = "ghcr.io/ferretdb/ferretdb:2.7.0" @@ -35,6 +64,22 @@ resource "kubernetes_deployment" "ferretdb" { container_port = 27017 name = "mongo" } + + port { + container_port = 8088 + name = "debug" + } + + resources { + requests = { + cpu = "250m" + memory = "256Mi" + } + limits = { + cpu = "500m" + memory = "500Mi" + } + } // PostgreSQL Certificates volume_mount { @@ -74,8 +119,21 @@ resource "kubernetes_deployment" "ferretdb" { tcp_socket { port = 27017 } - initial_delay_seconds = 30 + initial_delay_seconds = 10 + period_seconds = 10 + success_threshold = 3 + failure_threshold = 5 + } + + liveness_probe { + http_get { + path = "/debug/livez" + port = 8088 + } + initial_delay_seconds = 10 period_seconds = 10 + success_threshold = 3 + failure_threshold = 5 } } From 85756b8cdc1e0521a7ac86b9a895cffd96612c42 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 15:21:57 +0530 Subject: [PATCH 26/39] feat(ferretdb): ferret netpol implementation --- modules/ferretdb/networkpolicy.tf | 86 +++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/modules/ferretdb/networkpolicy.tf b/modules/ferretdb/networkpolicy.tf index 3824148..100a935 100644 --- a/modules/ferretdb/networkpolicy.tf +++ b/modules/ferretdb/networkpolicy.tf @@ -164,3 +164,89 @@ resource "kubernetes_network_policy" "cnpg_network_policy" { } } } + +// Network Policy for FerretDB +resource "kubernetes_network_policy" "ferret_network_policy" { + metadata { + name = "ferret-network-policy" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "networkpolicy" + } + } + + spec { + pod_selector { + match_labels = { + app = var.app_name + component = "pod" + "part-of" = "ferretdb" + } + } + + policy_types = ["Ingress", "Egress"] + + # -------------- INGRESS RULES -------------- # + # Rule 1: Allow ingress from allowed pods in trusted namespaces + ingress { + from { + namespace_selector { + match_expressions { + key = "kubernetes.io/metadata.name" + operator = "In" + values = concat(local.access_namespaces, [kubernetes_namespace.namespace.metadata[0].name]) + } + } + + pod_selector { + match_labels = { + "ferret-mongo-access" = true + } + } + } + + ports { + protocol = "TCP" + port = 27017 + } + } + + # -------------- EGRESS RULES -------------- # + # Rule 1: Allow egress to CNPG pods + egress { + to { + pod_selector { + match_labels = { + "cnpg.io/cluster" = var.cluster_name + } + } + } + + ports { + protocol = "TCP" + port = 5432 + } + } + + # Rule 2: Allow DNS resolution to KubeDNS + egress { + to { + namespace_selector { + match_labels = { + "kubernetes.io/metadata.name" = "kube-system" + } + } + pod_selector { + match_labels = { + "k8s-app" = "kube-dns" + } + } + } + ports { + protocol = "UDP" + port = 53 + } + } + } +} From 2098e657226f224f94ab06b0eca1aa4bbded31b5 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 15:22:07 +0530 Subject: [PATCH 27/39] feat(ferretdb): ferret service implementation --- modules/ferretdb/outputs.tf | 2 +- modules/ferretdb/service.tf | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 modules/ferretdb/service.tf diff --git a/modules/ferretdb/outputs.tf b/modules/ferretdb/outputs.tf index 3d7975d..556c621 100644 --- a/modules/ferretdb/outputs.tf +++ b/modules/ferretdb/outputs.tf @@ -1,5 +1,5 @@ output "namespace" { description = "Namespace where the PostgreSQL Database is deployed in" value = kubernetes_namespace.namespace.metadata[0].name - depends_on = [kubernetes_manifest.ferret_database] + depends_on = [kubernetes_deployment.ferretdb] } diff --git a/modules/ferretdb/service.tf b/modules/ferretdb/service.tf new file mode 100644 index 0000000..4492b9f --- /dev/null +++ b/modules/ferretdb/service.tf @@ -0,0 +1,27 @@ +resource "kubernetes_service" "ferret_service" { + metadata { + name = "ferret-service" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "service" + } + } + + spec { + selector = { + app = var.app_name + component = "pod" + "part-of" = "ferretdb" + } + + port { + name = "mongo" + port = 27017 + target_port = 27017 + protocol = "TCP" + } + + type = "ClusterIP" + } +} From 5f05b4726ecb8d6ff52f697d88d19393630eea41 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 15:23:22 +0530 Subject: [PATCH 28/39] feat(ferretdb): ferret pdb implementation --- modules/ferretdb/poddisruptionbudget.tf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/modules/ferretdb/poddisruptionbudget.tf b/modules/ferretdb/poddisruptionbudget.tf index 1f8cfd4..4b0d455 100644 --- a/modules/ferretdb/poddisruptionbudget.tf +++ b/modules/ferretdb/poddisruptionbudget.tf @@ -19,3 +19,27 @@ resource "kubernetes_pod_disruption_budget_v1" "cnpg_pdb" { depends_on = [kubernetes_manifest.cluster] } + +resource "kubernetes_pod_disruption_budget_v1" "ferret_pdb" { + metadata { + name = "ferret-pdb" + namespace = kubernetes_namespace.namespace.metadata[0].name + labels = { + app = var.app_name + component = "pdb" + } + } + + spec { + min_available = 1 + selector { + match_labels = { + app = var.app_name + component = "pod" + "part-of" = "ferretdb" + } + } + } + + depends_on = [kubernetes_deployment.ferretdb] +} From 231b7c57d2acca086d4d6e7e93290ac0b941bcd4 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 15:24:57 +0530 Subject: [PATCH 29/39] fix(ferretdb): liveness probe for ferret fixed --- modules/ferretdb/ferret.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index aa9b60b..d4d431c 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -132,7 +132,7 @@ resource "kubernetes_deployment" "ferretdb" { } initial_delay_seconds = 10 period_seconds = 10 - success_threshold = 3 + success_threshold = 1 failure_threshold = 5 } } From 2ed64d978ef82e83f2f61f9f1e25d676a5ac458b Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 22:16:12 +0530 Subject: [PATCH 30/39] fix(ferretdb): use superuser credentials since cnpg is isolated --- modules/ferretdb/ferret.tf | 8 +-- modules/ferretdb/job.tf | 104 ------------------------------------- 2 files changed, 4 insertions(+), 108 deletions(-) delete mode 100644 modules/ferretdb/job.tf diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index d4d431c..ce84d93 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -92,8 +92,8 @@ resource "kubernetes_deployment" "ferretdb" { name = "DB_USER" value_from { secret_key_ref { - name = kubernetes_secret.ferret_database_credentials.metadata[0].name - key = "username" + name = "${var.cluster_name}-superuser" + key = "user" } } } @@ -101,7 +101,7 @@ resource "kubernetes_deployment" "ferretdb" { name = "DB_PASS" value_from { secret_key_ref { - name = kubernetes_secret.ferret_database_credentials.metadata[0].name + name = "${var.cluster_name}-superuser" key = "password" } } @@ -112,7 +112,7 @@ resource "kubernetes_deployment" "ferretdb" { } env { name = "FERRETDB_POSTGRESQL_URL" - value = "postgres://$(DB_USER):$(DB_PASS)@$(DB_HOST):5432/ferret?sslmode=verify-ca&sslrootcert=/etc/certs/ca.crt" + value = "postgres://$(DB_USER):$(DB_PASS)@$(DB_HOST):5432/postgres?sslmode=verify-ca&sslrootcert=/etc/certs/ca.crt" } readiness_probe { diff --git a/modules/ferretdb/job.tf b/modules/ferretdb/job.tf deleted file mode 100644 index 1c13898..0000000 --- a/modules/ferretdb/job.tf +++ /dev/null @@ -1,104 +0,0 @@ -resource "kubernetes_job" "ferret_permissions" { - metadata { - name = "ferret-sql-permissions" - namespace = kubernetes_namespace.namespace.metadata[0].name - labels = { - app = var.app_name - component = "job" - } - } - - spec { - template { - metadata { - name = "ferret-sql-permissions" - labels = { - app = var.app_name - component = "pod" - "ferret-access" = "true" - } - } - - spec { - restart_policy = "OnFailure" - - container { - name = "grant-permissions" - image = "postgres:17-alpine" - - env { - name = "PGHOST" - value = "ferret-postgresql-cluster-rw" - } - env { - name = "PGDATABASE" - value = "ferret" - } - env { - name = "PGUSER" - value_from { - secret_key_ref { - name = "ferret-postgresql-cluster-superuser" - key = "username" - } - } - } - env { - name = "PGPASSWORD" - value_from { - secret_key_ref { - name = "ferret-postgresql-cluster-superuser" - key = "password" - } - } - } - - command = [ - "/bin/bash", - "-c" - ] - args = [ - < Date: Mon, 12 Jan 2026 22:21:45 +0530 Subject: [PATCH 31/39] fix(ferretdb): dependencies fix --- modules/ferretdb/ferret.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index ce84d93..4e5d3d0 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -151,5 +151,5 @@ resource "kubernetes_deployment" "ferretdb" { } } - depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database, kubernetes_job.ferret_permissions ] + depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database ] } From f69eda0c3a23495d46fec628117c7b14c5bc88f3 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 22:31:42 +0530 Subject: [PATCH 32/39] fix(ferretdb): cluster fix for ferret --- modules/ferretdb/cluster.tf | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 4c953cd..00a56cd 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -19,6 +19,7 @@ resource "kubernetes_manifest" "cluster" { } "postgresUID" = 999 "postgresGID" = 999 + "enableSuperuserAccess" = true "topologySpreadConstraints" = [ { "maxSkew" = 1 @@ -49,7 +50,14 @@ resource "kubernetes_manifest" "cluster" { ] "parameters" = { "search_path" = "\"$user\", public, documentdb_api, documentdb_core" - "cron.database_name" = "ferret" + "cron.database_name" = "postgres" + } + } + "bootstrap" = { + "initdb" = { + "postInitSQL" = [ + "CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;" + ] } } "managed" = { From baec8c1efdcebe0554c9d7b376a951325d489156 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 22:43:32 +0530 Subject: [PATCH 33/39] fix(ferretdb): pg_hba fix for ferretdb --- modules/ferretdb/cluster.tf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 00a56cd..00c93af 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -52,6 +52,9 @@ resource "kubernetes_manifest" "cluster" { "search_path" = "\"$user\", public, documentdb_api, documentdb_core" "cron.database_name" = "postgres" } + "pg_hba" = [ + "host postgres postgres 127.0.0.1/32 trust" + ] } "bootstrap" = { "initdb" = { From e329e3f0e1141537e0999802c42ed2e7482c4d9e Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 22:50:34 +0530 Subject: [PATCH 34/39] fix(ferretdb): pg_hba fix for ferretdb --- modules/ferretdb/cluster.tf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ferretdb/cluster.tf b/modules/ferretdb/cluster.tf index 00c93af..291a90c 100644 --- a/modules/ferretdb/cluster.tf +++ b/modules/ferretdb/cluster.tf @@ -53,7 +53,8 @@ resource "kubernetes_manifest" "cluster" { "cron.database_name" = "postgres" } "pg_hba" = [ - "host postgres postgres 127.0.0.1/32 trust" + "host postgres postgres 127.0.0.1/32 trust", + "host postgres postgres ::1/128 trust" ] } "bootstrap" = { From 785b8ddc47fe933e3bdaa056dc84383d6c1ad8d3 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 23:07:17 +0530 Subject: [PATCH 35/39] feat(ferretdb): cleanup --- modules/ferretdb/certificates.tf | 50 ------------------------ modules/ferretdb/database.tf | 66 -------------------------------- 2 files changed, 116 deletions(-) delete mode 100644 modules/ferretdb/database.tf diff --git a/modules/ferretdb/certificates.tf b/modules/ferretdb/certificates.tf index 30c9a20..5bd598e 100644 --- a/modules/ferretdb/certificates.tf +++ b/modules/ferretdb/certificates.tf @@ -295,53 +295,3 @@ resource "kubernetes_manifest" "client_streaming_replica_certificate" { delete = "5m" } } - -// Certificate for Ferret User -resource "kubernetes_manifest" "client_ferret_certificate" { - manifest = { - "apiVersion" = "cert-manager.io/v1" - "kind" = "Certificate" - "metadata" = { - "name" = "postgresql-ferret-client-certificate" - "namespace" = kubernetes_namespace.namespace.metadata[0].name - "labels" = { - "app" = var.app_name - "component" = "certificate" - } - } - "spec" = { - "usages" : ["client auth"] - "subject" = { - "organizations" = [var.organization_name] - "countries" = [var.country_name] - "organizationalUnits" = ["PostgreSQL"] - } - "commonName" = "ferret" - "secretName" = "postgresql-ferret-client-certificate" - "additionalOutputFormats" = [ - { - "type" : "DER" - } - ] - "privateKey" = { - "encoding" = "PKCS8" - } - "issuerRef" = { - "name" = kubernetes_manifest.client_issuer.manifest.metadata.name - } - } - } - - wait { - condition { - type = "Ready" - status = "True" - } - } - - timeouts { - create = "5m" - update = "5m" - delete = "5m" - } -} diff --git a/modules/ferretdb/database.tf b/modules/ferretdb/database.tf deleted file mode 100644 index b8de56d..0000000 --- a/modules/ferretdb/database.tf +++ /dev/null @@ -1,66 +0,0 @@ -// Database Configuration for Ferret -resource "kubernetes_manifest" "ferret_database" { - manifest = { - "apiVersion" : "postgresql.cnpg.io/v1" - "kind" : "Database" - "metadata" = { - "name" = "ferret" - "namespace" = kubernetes_namespace.namespace.metadata[0].name - "labels" = { - "app" = var.app_name - "component" = "cluster" - } - } - - "spec" = { - "databaseReclaimPolicy" : "delete" - "name" : "ferret" - "owner" : "ferret" - "cluster" = { - "name" = kubernetes_manifest.cluster.manifest.metadata.name - } - "extensions" = [ - { - "name" = "vector" - "ensure" = "present" - }, - { - "name" = "tsm_system_rows" - "ensure" = "present" - }, - { - "name" = "postgis" - "ensure" = "present" - }, - { - "name" = "rum" - "ensure" = "present" - }, - { - "name" = "pg_cron" - "ensure" = "present" - }, - { - "name" = "documentdb_core" - "ensure" = "present" - }, - { - "name" = "documentdb" - "ensure" = "present" - } - ] - } - } - - wait { - fields = { - "status.applied" = "true" - } - } - - timeouts { - create = "5m" - update = "5m" - delete = "5m" - } -} From 9b3ff7711f2d52eeca20828bea0071700a2bd055 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 23:08:45 +0530 Subject: [PATCH 36/39] fix(ferretdb): dependencies fix --- modules/ferretdb/ferret.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ferretdb/ferret.tf b/modules/ferretdb/ferret.tf index 4e5d3d0..082e89b 100644 --- a/modules/ferretdb/ferret.tf +++ b/modules/ferretdb/ferret.tf @@ -151,5 +151,5 @@ resource "kubernetes_deployment" "ferretdb" { } } - depends_on = [ kubernetes_manifest.cluster, kubernetes_manifest.ferret_database ] + depends_on = [ kubernetes_manifest.cluster ] } From 9abed73cedffc63378ae4f5fa2e519f8d09c8ff7 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 23:29:25 +0530 Subject: [PATCH 37/39] docs(ferretdb): README update --- modules/ferretdb/README.md | 74 +++++++++++++------------------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/modules/ferretdb/README.md b/modules/ferretdb/README.md index ad72e77..0f21db0 100644 --- a/modules/ferretdb/README.md +++ b/modules/ferretdb/README.md @@ -1,8 +1,8 @@ -## necronizer's cloud cloudnative pg module +## necronizer's cloud ferret module -OpenTofu Module to deploy [Cloudnative PG](https://cloudnative-pg.io/) PostgreSQL Database on the Kubernetes Cluster +OpenTofu Module to deploy [FerretDB](https://www.ferretdb.com/) (MongoDB) Database on the Kubernetes Cluster -Required Modules to deploy Cloudnative PG PostgreSQL Database: +Required Modules to deploy FerretDB Database: 1. [Helm](../helm) 2. [Cluster Issuer](../cluster-issuer) 3. [Garage](../garage) @@ -18,84 +18,58 @@ Required Modules to deploy Cloudnative PG PostgreSQL Database: | Name | Type | |------|------| -| [kubernetes_config_map.nginx_conf](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource | -| [kubernetes_config_map.pgadmin_servers_configuration](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource | -| [kubernetes_deployment.pgadmin](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | -| [kubernetes_ingress_v1.api_ingress](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/ingress_v1) | resource | +| [kubernetes_deployment.ferretdb](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/deployment) | resource | | [kubernetes_manifest.barman_object_store](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.client_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.client_certificates](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.client_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.client_keycloak_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.client_streaming_replica_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.cluster](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.cluster_image_catalog](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.databases](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.ingress_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.keycloak_database](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.pgadmin_internal_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | -| [kubernetes_manifest.public_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | +| [kubernetes_manifest.ferret_cluster_image_catalog](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.server_certificate](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.server_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_manifest.server_issuer](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/manifest) | resource | | [kubernetes_namespace.namespace](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource | | [kubernetes_network_policy.cnpg_network_policy](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/network_policy) | resource | +| [kubernetes_network_policy.ferret_network_policy](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/network_policy) | resource | | [kubernetes_pod_disruption_budget_v1.cnpg_pdb](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/pod_disruption_budget_v1) | resource | +| [kubernetes_pod_disruption_budget_v1.ferret_pdb](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/pod_disruption_budget_v1) | resource | | [kubernetes_secret.client_database_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | -| [kubernetes_secret.cloudflare_token](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | +| [kubernetes_secret.ferret_database_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | | [kubernetes_secret.garage_certificate_authority](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | | [kubernetes_secret.garage_configuration](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | -| [kubernetes_secret.keycloak_database_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | -| [kubernetes_secret.pgadmin_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret) | resource | -| [kubernetes_service.pgadmin4](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service) | resource | +| [kubernetes_service.ferret_service](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service) | resource | | [random_password.client_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | -| [random_password.keycloak_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | -| [random_password.pgadmin_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | +| [random_password.ferret_password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [acme\_server](#input\_acme\_server) | URL for the ACME Server to be used, defaults to production URL for LetsEncrypt | `string` | `"https://acme-v02.api.letsencrypt.org/directory"` | no | -| [app\_name](#input\_app\_name) | App name for deploying PostgreSQL Database | `string` | `"postgres"` | no | +| [app\_name](#input\_app\_name) | App name for deploying Ferret Database | `string` | `"ferret"` | no | | [backup\_bucket\_name](#input\_backup\_bucket\_name) | Name of the bucket for storing PITR Backups in Garage | `string` | n/a | yes | -| [client\_certificate\_authority\_name](#input\_client\_certificate\_authority\_name) | Name of the Certificate Authority to be used with PostgreSQL Client | `string` | `"postgresql-client-certificate-authority"` | no | -| [client\_issuer\_name](#input\_client\_issuer\_name) | Name of the Issuer to be used with PostgreSQL Client | `string` | `"postgresql-client-issuer"` | no | -| [client\_streaming\_replica\_certificate\_name](#input\_client\_streaming\_replica\_certificate\_name) | Name of the Certificate to be used with PostgreSQL Streaming Replica Client | `string` | `"postgresql-streaming-replica-client-certificate"` | no | +| [client\_certificate\_authority\_name](#input\_client\_certificate\_authority\_name) | Name of the Certificate Authority to be used with Ferret Client | `string` | `"ferretdb-client-certificate-authority"` | no | +| [client\_issuer\_name](#input\_client\_issuer\_name) | Name of the Issuer to be used with Ferret Client | `string` | `"ferretdb-client-issuer"` | no | +| [client\_streaming\_replica\_certificate\_name](#input\_client\_streaming\_replica\_certificate\_name) | Name of the Certificate to be used with Ferret Streaming Replica Client | `string` | `"ferretdb-streaming-replica-client-certificate"` | no | | [clients](#input\_clients) | Object List of clients who need databases and users to be configured for |
list(object({
namespace = string
user = string
database = string
derRequired = bool
privateKeyEncoding = string
}))
| `[]` | no | -| [cloudflare\_email](#input\_cloudflare\_email) | Email for generating Ingress Certificates to be associated with PGAdmin | `string` | n/a | yes | -| [cloudflare\_issuer\_name](#input\_cloudflare\_issuer\_name) | Name of the Cloudflare Issuer to be associated with PGAdmin | `string` | `"cnpg-cloudflare-issuer"` | no | -| [cloudflare\_token](#input\_cloudflare\_token) | Token for generating Ingress Certificates to be associated with PGAdmin | `string` | n/a | yes | | [cluster\_issuer\_name](#input\_cluster\_issuer\_name) | Name for the Cluster Issuer to be used to generate internal self signed certificates | `string` | n/a | yes | -| [cluster\_name](#input\_cluster\_name) | Name of the PostgreSQL Database Cluster to be created | `string` | `"postgresql-cluster"` | no | -| [cluster\_postgresql\_version](#input\_cluster\_postgresql\_version) | Version of PostgreSQL Database to use and deploy | `number` | `17` | no | -| [cluster\_size](#input\_cluster\_size) | Number of pods to deploy for the PostgreSQL Cluster | `number` | `2` | no | -| [country\_name](#input\_country\_name) | Country name for deploying PostgreSQL Database | `string` | `"India"` | no | -| [domain](#input\_domain) | Domain for which Ingress Certificate is to be generated for | `string` | n/a | yes | +| [cluster\_name](#input\_cluster\_name) | Name of the Ferret Database Cluster to be created | `string` | `"ferret-postgresql-cluster"` | no | +| [cluster\_postgresql\_version](#input\_cluster\_postgresql\_version) | Version of Ferret Database to use and deploy | `number` | `17` | no | +| [cluster\_size](#input\_cluster\_size) | Number of pods to deploy for the Ferret Cluster | `number` | `2` | no | +| [country\_name](#input\_country\_name) | Country name for deploying Ferret Database | `string` | `"India"` | no | | [garage\_certificate\_authority](#input\_garage\_certificate\_authority) | Name of the Certificate Authority associated with the Garage Storage Solution | `string` | n/a | yes | | [garage\_configuration](#input\_garage\_configuration) | Garage Configuration for storing PITR Backups | `string` | n/a | yes | | [garage\_namespace](#input\_garage\_namespace) | Namespace for the Garage Deployment for storing PITR Backups | `string` | n/a | yes | -| [host\_name](#input\_host\_name) | Host name for which Ingress Certificate is to be generated for | `string` | `"sql"` | no | -| [image](#input\_image) | Docker image to be used for deployment of PGAdmin | `string` | `"pgadmin4"` | no | -| [ingress\_certificate\_name](#input\_ingress\_certificate\_name) | Name of the Ingress Certificate to be associated with PGAdmin | `string` | `"pgadmin-ingress-certificate"` | no | | [kubernetes\_api\_ip](#input\_kubernetes\_api\_ip) | IP Address for the Kubernetes API | `string` | n/a | yes | | [kubernetes\_api\_port](#input\_kubernetes\_api\_port) | Port for the Kubernetes API | `number` | n/a | yes | | [kubernetes\_api\_protocol](#input\_kubernetes\_api\_protocol) | Protocol for the Kubernetes API | `string` | n/a | yes | -| [namespace](#input\_namespace) | Namespace to be used for deploying PostgreSQL Database | `string` | `"postgres"` | no | -| [organization\_name](#input\_organization\_name) | Organization name for deploying PostgreSQL Database | `string` | `"cloud"` | no | -| [proxy\_image](#input\_proxy\_image) | Docker image to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"nginx"` | no | -| [proxy\_repository](#input\_proxy\_repository) | Repository to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"docker.io/library"` | no | -| [proxy\_tag](#input\_proxy\_tag) | Docker tag to be used for deployment of PGAdmin NGINX Proxy for TLS | `string` | `"1.29.0"` | no | -| [repository](#input\_repository) | Repository to be used for deployment of PGAdmin | `string` | `"docker.io/dpage"` | no | -| [server\_certificate\_authority\_name](#input\_server\_certificate\_authority\_name) | Name of the Certificate Authority to be used with PostgreSQL Server | `string` | `"postgresql-server-certificate-authority"` | no | -| [server\_certificate\_name](#input\_server\_certificate\_name) | Name of the Certificate to be used with PostgreSQL Server | `string` | `"postgresql-server-certificate"` | no | -| [server\_issuer\_name](#input\_server\_issuer\_name) | Name of the Issuer to be used with PostgreSQL Server | `string` | `"postgresql-server-issuer"` | no | -| [tag](#input\_tag) | Docker tag to be used for deployment of PGAdmin | `string` | `"9.7.0"` | no | +| [namespace](#input\_namespace) | Namespace to be used for deploying Ferret Database | `string` | `"ferret"` | no | +| [organization\_name](#input\_organization\_name) | Organization name for deploying Ferret Database | `string` | `"cloud"` | no | +| [server\_certificate\_authority\_name](#input\_server\_certificate\_authority\_name) | Name of the Certificate Authority to be used with Ferret Server | `string` | `"ferretdb-server-certificate-authority"` | no | +| [server\_certificate\_name](#input\_server\_certificate\_name) | Name of the Certificate to be used with Ferret Server | `string` | `"ferretdb-server-certificate"` | no | +| [server\_issuer\_name](#input\_server\_issuer\_name) | Name of the Issuer to be used with Ferret Server | `string` | `"ferretdb-server-issuer"` | no | ## Outputs | Name | Description | |------|-------------| -| [cluster\_name](#output\_cluster\_name) | Name of the CNPG PostgreSQL Cluster | -| [namespace](#output\_namespace) | Namespace where the PostgreSQL Database is deployed in | -| [server-certificate-authority](#output\_server-certificate-authority) | Certificate Authority being used with PostgreSQL Database | +| [namespace](#output\_namespace) | Namespace where FerretDB is deployed in | From 1b3f50482b9f820e54f9553682a169627c6a1303 Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 23:35:01 +0530 Subject: [PATCH 38/39] feat(example): move to main --- example/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/main.tf b/example/main.tf index 910120f..edc174f 100644 --- a/example/main.tf +++ b/example/main.tf @@ -81,7 +81,7 @@ module "cnpg" { # FerretDB Deployment for MongoDB Database Solution module "ferretdb" { - source = "git::https://github.com/necro-cloud/modules//modules/ferretdb?ref=task/65/ferretdb-setup" + source = "git::https://github.com/necro-cloud/modules//modules/ferretdb?ref=main" // Garage Cluster Details for configuration of PITR Backups garage_certificate_authority = module.garage.garage_internal_certificate_secret From 0e074cfe2ca8db49d6be777ba06e060a16d70e5e Mon Sep 17 00:00:00 2001 From: khatrivarun Date: Mon, 12 Jan 2026 23:39:35 +0530 Subject: [PATCH 39/39] docs: readme update --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 40baec3..59fc9a5 100644 --- a/README.md +++ b/README.md @@ -18,5 +18,6 @@ The following modules have been implemented and their usage instructions written 3. [\[DEPRECATED\] MinIO Storage](modules/minio) 4. [Garage Storage](modules/garage) 5. [Cloudnative PG PostgreSQL Database](modules/cnpg) -6. [Valkey In Memory Database](modules/valkey) -7. [Keycloak Identity Management](modules/keycloak) +6. [FerretDB (MongoDB) Database](modules/ferretdb) +7. [Valkey In Memory Database](modules/valkey) +8. [Keycloak Identity Management](modules/keycloak)