From 5bfb72ef81a6a2f6f70ab774ecd3adc458c8af7f Mon Sep 17 00:00:00 2001 From: Shahab Dogar Date: Mon, 14 Jul 2025 21:09:38 +0500 Subject: [PATCH] feat: CDKTF | migrate postgres to CDKTF --- helm/helmfile.yaml | 8 - main.ts | 14 ++ postgres/certificates.yaml | 125 --------------- postgres/cluster.yaml | 33 ---- postgres/postgres.ts | 308 +++++++++++++++++++++++++++++++++++++ 5 files changed, 322 insertions(+), 166 deletions(-) delete mode 100644 postgres/certificates.yaml delete mode 100644 postgres/cluster.yaml create mode 100644 postgres/postgres.ts diff --git a/helm/helmfile.yaml b/helm/helmfile.yaml index afc3aa9..32ae591 100644 --- a/helm/helmfile.yaml +++ b/helm/helmfile.yaml @@ -9,8 +9,6 @@ repositories: url: https://kubernetes.github.io/ingress-nginx - name: bitnami url: https://charts.bitnami.com/bitnami - - name: cnpg - url: https://cloudnative-pg.github.io/charts - name: jetstack url: https://charts.jetstack.io - name: prometheus-community @@ -57,12 +55,6 @@ releases: values: - ./values/externaldns.values.yaml - # Postgres operator - - name: postgres-system - namespace: postgres-system - chart: cnpg/cloudnative-pg - version: 0.23.0 - # Memcached - name: memcached namespace: memcached-system diff --git a/main.ts b/main.ts index 4fdff89..c72acaf 100644 --- a/main.ts +++ b/main.ts @@ -7,6 +7,7 @@ import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; import { GiteaServer } from "./gitea/server"; import { OnePassword } from "./1password/1password"; +import { PostgresCluster } from "./postgres/postgres"; dotenv.config(); @@ -31,6 +32,19 @@ class Homelab extends TerraformStack { }, }); + new PostgresCluster(this, "postgres-cluster", { + name: "postgres-cluster", + namespace: "postgres-system", + providers: { + kubernetes, + helm, + }, + storageClass: "longhorn-crypto", + users: ["shahab"], + primaryUser: "shahab", + initSecretName: "postgres-password", + }); + new GiteaServer(this, "gitea-server", { name: "gitea", namespace: "gitea-system", diff --git a/postgres/certificates.yaml b/postgres/certificates.yaml deleted file mode 100644 index 698356b..0000000 --- a/postgres/certificates.yaml +++ /dev/null @@ -1,125 +0,0 @@ ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: selfsigned-issuer - namespace: postgres-system -spec: - selfSigned: {} ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: server-ca - namespace: postgres-system -spec: - isCA: true - commonName: postgres-server-ca - secretName: postgres-server-ca - privateKey: - algorithm: ECDSA - size: 384 - issuerRef: - name: selfsigned-issuer - kind: Issuer - group: cert-manager.io ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: postgres-server-ca-issuer - namespace: postgres-system -spec: - ca: - secretName: postgres-server-ca ---- -apiVersion: v1 -kind: Secret -metadata: - name: postgres-server-cert - namespace: postgres-system - labels: - cnpg.io/reload: "" ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: postgres-server-cert - namespace: postgres-system -spec: - secretName: postgres-server-cert - usages: - - server auth - dnsNames: - - postgres-cluster-rw.postgres-system.svc.cluster.local - - postgres-cluster-ro.postgres-system.svc.cluster.local - - postgres-cluster-r.postgres-system.svc.cluster.local - - postgres.dogar.dev - issuerRef: - name: postgres-server-ca-issuer - kind: Issuer - group: cert-manager.io ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: client-ca - namespace: postgres-system -spec: - isCA: true - commonName: postgres-client-ca - secretName: postgres-client-ca - privateKey: - algorithm: ECDSA - size: 256 - issuerRef: - name: selfsigned-issuer - kind: Issuer - group: cert-manager.io ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: postgres-client-ca-issuer - namespace: postgres-system -spec: - ca: - secretName: postgres-client-ca ---- -apiVersion: v1 -kind: Secret -metadata: - name: postgres-client-cert - namespace: postgres-system - labels: - cnpg.io/reload: "" ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: postgres-client-cert - namespace: postgres-system -spec: - secretName: postgres-client-cert - usages: - - client auth - commonName: streaming_replica - issuerRef: - name: postgres-client-ca-issuer - kind: Issuer - group: cert-manager.io ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: shahab-client-cert - namespace: postgres-system -spec: - secretName: shahab-client-cert - usages: - - client auth - commonName: shahab - issuerRef: - name: postgres-client-ca-issuer - kind: Issuer - group: cert-manager.io diff --git a/postgres/cluster.yaml b/postgres/cluster.yaml deleted file mode 100644 index 4142827..0000000 --- a/postgres/cluster.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -apiVersion: postgresql.cnpg.io/v1 -kind: Cluster -metadata: - name: postgres-cluster - namespace: postgres-system -spec: - instances: 3 - maxSyncReplicas: 0 - primaryUpdateStrategy: unsupervised - certificates: - serverCASecret: postgres-server-cert - serverTLSSecret: postgres-server-cert - clientCASecret: postgres-client-cert - replicationTLSSecret: postgres-client-cert - postgresql: - pg_hba: - - hostssl all shahab all cert - - hostssl sameuser all all cert - enableSuperuserAccess: false - bootstrap: - initdb: - database: postgres - secret: - name: postgres-password - postInitSQL: - - 'CREATE USER shahab SUPERUSER;' - storage: - size: 10Gi - storageClass: longhorn-crypto - walStorage: - size: 10Gi - storageClass: longhorn-crypto diff --git a/postgres/postgres.ts b/postgres/postgres.ts new file mode 100644 index 0000000..ff54914 --- /dev/null +++ b/postgres/postgres.ts @@ -0,0 +1,308 @@ +import { HelmProvider } from "@cdktf/provider-helm/lib/provider"; +import { Release } from "@cdktf/provider-helm/lib/release"; +import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest"; +import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; +import { Construct } from "constructs"; + +type PostgresClusterOptions = { + providers: { + kubernetes: KubernetesProvider; + helm: HelmProvider; + }; + name: string; + namespace: string; + storageClass: string; + users: string[]; + primaryUser: string; + initSecretName: string; +}; + +export class PostgresCluster extends Construct { + constructor(scope: Construct, id: string, options: PostgresClusterOptions) { + super(scope, id); + + const { kubernetes, helm } = options.providers; + + new Release(this, "cnpg-operator", { + provider: helm, + repository: "https://cloudnative-pg.github.io/charts", + chart: "cloudnative-pg", + name: "postgres-system", + namespace: options.namespace, + }); + + const certManagerApiVersion = "cert-manager.io/v1"; + + const certNames = { + server: "postgres-server-cert", + client: "postgres-client-cert", + }; + + const caNames = { + server: "postgres-server-ca", + client: "postgres-client-ca", + }; + + // Self-signed issuer for creating CA certificates + new Manifest(this, "selfsigned-issuer", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Issuer", + metadata: { + name: "selfsigned-issuer", + namespace: options.namespace, + }, + spec: { + selfSigned: {}, + }, + }, + }); + + // Server CA certificate + new Manifest(this, "server-ca-cert", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Certificate", + metadata: { + name: "server-ca", + namespace: options.namespace, + }, + spec: { + isCA: true, + commonName: caNames.server, + secretName: caNames.server, + privateKey: { + algorithm: "ECDSA", + size: 384, + }, + issuerRef: { + name: "selfsigned-issuer", + kind: "Issuer", + group: "cert-manager.io", + }, + }, + }, + }); + + // Issuer using the server CA + new Manifest(this, "server-ca-issuer", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Issuer", + metadata: { + name: `${caNames.server}-issuer`, + namespace: options.namespace, + }, + spec: { + ca: { + secretName: caNames.server, + }, + }, + }, + }); + + // Secret for server certificate + new Manifest(this, "server-ca-cert-secret", { + provider: kubernetes, + manifest: { + apiVersion: "v1", + kind: "Secret", + metadata: { + name: certNames.server, + namespace: options.namespace, + labels: { + "cnpg.io/reload": "", + }, + }, + }, + }); + + // Server certificate + new Manifest(this, "server-cert", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Certificate", + metadata: { + name: certNames.server, + namespace: options.namespace, + }, + spec: { + secretName: certNames.server, + usages: ["server auth"], + dnsNames: [ + "postgres-cluster-rw.postgres-system.svc.cluster.local", + "postgres-cluster-ro.postgres-system.svc.cluster.local", + "postgres-cluster-r.postgres-system.svc.cluster.local", + "postgres.dogar.dev", + ], + issuerRef: { + name: `${caNames.server}-issuer`, + kind: "Issuer", + group: "cert-manager.io", + }, + }, + }, + }); + + // Client CA certificate + new Manifest(this, "client-ca", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Certificate", + metadata: { + name: "client-ca", + namespace: options.namespace, + }, + spec: { + isCA: true, + commonName: caNames.client, + secretName: caNames.client, + privateKey: { + algorithm: "ECDSA", + size: 256, + }, + issuerRef: { + name: "selfsigned-issuer", + kind: "Issuer", + group: "cert-manager.io", + }, + }, + }, + }); + + // Issuer using the client CA + new Manifest(this, "client-ca-issuer", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Issuer", + metadata: { + name: `${caNames.client}-issuer`, + namespace: options.namespace, + }, + spec: { + ca: { + secretName: caNames.client, + }, + }, + }, + }); + + // Secret for client certificate + new Manifest(this, `${certNames.client}-secret`, { + provider: kubernetes, + manifest: { + apiVersion: "v1", + kind: "Secret", + metadata: { + name: certNames.client, + namespace: options.namespace, + labels: { + "cnpg.io/reload": "", + }, + }, + }, + }); + + // Client certificate for streaming replica + new Manifest(this, "streaming-replica-cert", { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Certificate", + metadata: { + name: certNames.client, + namespace: options.namespace, + }, + spec: { + secretName: certNames.client, + usages: ["client auth"], + commonName: "streaming_replica", + issuerRef: { + name: "postgres-client-ca-issuer", + kind: "Issuer", + group: "cert-manager.io", + }, + }, + }, + }); + + // Client certificates for users + options.users.forEach( + (user) => + new Manifest(this, `${user}-client-cert`, { + provider: kubernetes, + manifest: { + apiVersion: certManagerApiVersion, + kind: "Certificate", + metadata: { + name: `${user}-client-cert`, + namespace: options.namespace, + }, + spec: { + secretName: `${user}-client-cert`, + usages: ["client auth"], + commonName: user, + issuerRef: { + name: "postgres-client-ca-issuer", + kind: "Issuer", + group: "cert-manager.io", + }, + }, + }, + }), + ); + + new Manifest(this, "postgres-cluster", { + provider: kubernetes, + manifest: { + apiVersion: "postgresql.cnpg.io/v1", + kind: "Cluster", + metadata: { + name: options.name, + namespace: options.namespace, + }, + spec: { + instances: 3, + maxSyncReplicas: 0, + primaryUpdateStrategy: "unsupervised", + certificates: { + serverCASecret: certNames.server, + serverTLSSecret: certNames.server, + clientCASecret: certNames.client, + replicationTLSSecret: certNames.client, + }, + postgresql: { + pg_hba: [ + `hostssl all ${options.primaryUser} all cert`, + "hostssl sameuser all all cert", + ], + }, + enableSuperuserAccess: false, + bootstrap: { + initdb: { + database: "postgres", + secret: { + name: options.initSecretName, + }, + postInitSQL: [`CREATE USER ${options.primaryUser} SUPERUSER;`], + }, + }, + storage: { + size: "10Gi", + storageClass: options.storageClass, + }, + walStorage: { + size: "10Gi", + storageClass: options.storageClass, + }, + }, + }, + }); + } +}