From 5de64c5b1fe70ae639520f9ded73fb6872d27eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Trkan?= Date: Mon, 22 Sep 2025 18:51:52 +0200 Subject: [PATCH] feat(infrastructure): add cloudflare, argocd --- tofu/main.tf | 53 +++++++++++++++---- tofu/modules/argocd/argocd-ui.yaml | 14 +++++ tofu/modules/argocd/main.tf | 43 +++++++++++++++ tofu/modules/argocd/variables.tf | 12 +++++ tofu/modules/cert-manager/main.tf | 30 +++++++++++ tofu/modules/cloudflare/cluster-tunnel.yaml | 12 +++++ .../kustomization/kustomization.yaml | 5 ++ tofu/modules/cloudflare/main.tf | 50 +++++++++++++++++ tofu/modules/cloudflare/secret.yaml | 8 +++ tofu/modules/cloudflare/variables.tf | 30 +++++++++++ .../maxscale/charts/maxscale-helm/Chart.yaml | 2 +- .../maxscale-helm/templates/maxscale-ui.yaml | 14 +++++ .../templates/phpmyadmin-config-map.yaml | 20 +++---- .../maxscale-helm/templates/phpmyadmin.yaml | 14 +++++ .../maxscale/charts/maxscale-helm/values.yaml | 2 + tofu/modules/maxscale/main.tf | 3 +- tofu/modules/maxscale/variables.tf | 6 +++ tofu/terraform.tfvars.example | 7 +++ tofu/variables.tf | 38 +++++++++++++ 19 files changed, 340 insertions(+), 23 deletions(-) create mode 100644 tofu/modules/argocd/argocd-ui.yaml create mode 100644 tofu/modules/argocd/main.tf create mode 100644 tofu/modules/argocd/variables.tf create mode 100644 tofu/modules/cert-manager/main.tf create mode 100644 tofu/modules/cloudflare/cluster-tunnel.yaml create mode 100644 tofu/modules/cloudflare/kustomization/kustomization.yaml create mode 100644 tofu/modules/cloudflare/main.tf create mode 100644 tofu/modules/cloudflare/secret.yaml create mode 100644 tofu/modules/cloudflare/variables.tf create mode 100644 tofu/modules/maxscale/charts/maxscale-helm/templates/maxscale-ui.yaml create mode 100644 tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin.yaml diff --git a/tofu/main.tf b/tofu/main.tf index 3fa6ddc..1edc241 100644 --- a/tofu/main.tf +++ b/tofu/main.tf @@ -9,9 +9,13 @@ terraform { version = "3.0.2" } kubernetes = { - source = "hashicorp/kubernetes" + source = "hashicorp/kubernetes" version = "2.38.0" } + kustomization = { + source = "kbst/kustomization" + version = "0.9.6" + } } } @@ -23,6 +27,9 @@ provider "kubectl" { config_path = "./kubeconfig" } +provider "kustomization" { + kubeconfig_path = "./kubeconfig" +} provider "helm" { kubernetes = { @@ -30,17 +37,36 @@ provider "helm" { } } +module "storage" { + source = "${path.module}/modules/storage" +} + module "loadbalancer" { - source = "${path.module}/modules/metallb" - depends_on = [ module.storage ] + source = "${path.module}/modules/metallb" + depends_on = [module.storage] metallb_ip_range = var.metallb_ip_range } +module "cert-manager" { + source = "${path.module}/modules/cert-manager" +} + +module "cloudflare" { + source = "${path.module}/modules/cloudflare" + depends_on = [module.cert-manager] + + cloudflare_api_token = var.cloudflare_api_token + cloudflare_tunnel_name = var.cloudflare_tunnel_name + cloudflare_email = var.cloudflare_email + cloudflare_domain = var.cloudflare_domain + cloudflare_account_id = var.cloudflare_account_id +} + module "database" { source = "${path.module}/modules/maxscale" - depends_on = [ module.storage, module.loadbalancer ] + depends_on = [module.storage, module.loadbalancer, module.cloudflare] mariadb_password = var.mariadb_password mariadb_root_password = var.mariadb_root_password @@ -48,14 +74,19 @@ module "database" { mariadb_user_host = var.mariadb_user_host mariadb_user_password = var.mariadb_user_password - maxscale_ip = var.metallb_maxscale_ip - service_ip = var.metallb_service_ip - primary_ip = var.metallb_primary_ip - secondary_ip = var.metallb_secondary_ip + maxscale_ip = var.metallb_maxscale_ip + service_ip = var.metallb_service_ip + primary_ip = var.metallb_primary_ip + secondary_ip = var.metallb_secondary_ip - phpmyadmin_enabled = var.phpmyadmin_enabled + phpmyadmin_enabled = var.phpmyadmin_enabled + cloudflare_domain = var.cloudflare_domain } -module "storage" { - source = "${path.module}/modules/storage" +module "argocd" { + source = "${path.module}/modules/argocd" + depends_on = [module.storage, module.loadbalancer, module.cloudflare] + + argocd_admin_password = var.argocd_admin_password + cloudflare_domain = var.cloudflare_domain } \ No newline at end of file diff --git a/tofu/modules/argocd/argocd-ui.yaml b/tofu/modules/argocd/argocd-ui.yaml new file mode 100644 index 0000000..e9f5c6b --- /dev/null +++ b/tofu/modules/argocd/argocd-ui.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.cfargotunnel.com/v1alpha1 +kind: TunnelBinding +metadata: + name: argocd-tunnel-binding + namespace: argocd +subjects: + - name: argocd-server + spec: + target: https://argocd-server.argocd.svc.cluster.local + fqdn: argocd.${base_domain} + noTlsVerify: true +tunnelRef: + kind: ClusterTunnel + name: cluster-tunnel \ No newline at end of file diff --git a/tofu/modules/argocd/main.tf b/tofu/modules/argocd/main.tf new file mode 100644 index 0000000..9a2dab5 --- /dev/null +++ b/tofu/modules/argocd/main.tf @@ -0,0 +1,43 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "1.19.0" + } + helm = { + source = "hashicorp/helm" + version = "3.0.2" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.38.0" + } + } +} + +resource "kubernetes_namespace" "argocd" { + metadata { + name = "argocd" + } +} + +resource "helm_release" "argocd" { + name = "argocd" + namespace = "argocd" + repository = "https://argoproj.github.io/argo-helm" + chart = "argo-cd" + depends_on = [kubernetes_namespace.argocd] + + set = [ + { name = "configs.secret.argocdServerAdminPassword", value = bcrypt(var.argocd_admin_password) }, + ] +} + +resource "kubectl_manifest" "argocd-tunnel-bind" { + depends_on = [helm_release.argocd] + + yaml_body = templatefile("${path.module}/argocd-ui.yaml", { + base_domain = var.cloudflare_domain + }) +} + diff --git a/tofu/modules/argocd/variables.tf b/tofu/modules/argocd/variables.tf new file mode 100644 index 0000000..ba8b168 --- /dev/null +++ b/tofu/modules/argocd/variables.tf @@ -0,0 +1,12 @@ +variable "argocd_admin_password" { + type = string + nullable = false + sensitive = true + description = "ArgoCD admin password" +} + +variable "cloudflare_domain" { + type = string + default = "Base cloudflare domain, e.g. example.com" + nullable = false +} \ No newline at end of file diff --git a/tofu/modules/cert-manager/main.tf b/tofu/modules/cert-manager/main.tf new file mode 100644 index 0000000..6037f91 --- /dev/null +++ b/tofu/modules/cert-manager/main.tf @@ -0,0 +1,30 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "1.19.0" + } + helm = { + source = "hashicorp/helm" + version = "3.0.2" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.38.0" + } + } +} + +resource "helm_release" "cert_manager" { + name = "cert-manager" + repository = "https://charts.jetstack.io" + chart = "cert-manager" + version = "v1.14.4" + namespace = "cert-manager" + create_namespace = true + + set = [{ + name = "installCRDs" + value = "true" + }] +} diff --git a/tofu/modules/cloudflare/cluster-tunnel.yaml b/tofu/modules/cloudflare/cluster-tunnel.yaml new file mode 100644 index 0000000..dbae0fa --- /dev/null +++ b/tofu/modules/cloudflare/cluster-tunnel.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.cfargotunnel.com/v1alpha2 +kind: ClusterTunnel +metadata: + name: cluster-tunnel # The ClusterTunnel Custom Resource Name +spec: + newTunnel: + name: ${cloudflare_tunnel_name} # Name of your new tunnel on Cloudflare + cloudflare: + email: ${cloudflare_email} + domain: ${cloudflare_domain} + secret: cloudflare-secrets + accountId: ${cloudflare_account_id} \ No newline at end of file diff --git a/tofu/modules/cloudflare/kustomization/kustomization.yaml b/tofu/modules/cloudflare/kustomization/kustomization.yaml new file mode 100644 index 0000000..f2b1fc1 --- /dev/null +++ b/tofu/modules/cloudflare/kustomization/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: cloudflare-operator-system +resources: + - https://github.com/adyanth/cloudflare-operator.git/config/default?ref=v0.13.1 \ No newline at end of file diff --git a/tofu/modules/cloudflare/main.tf b/tofu/modules/cloudflare/main.tf new file mode 100644 index 0000000..a58890a --- /dev/null +++ b/tofu/modules/cloudflare/main.tf @@ -0,0 +1,50 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "1.19.0" + } + helm = { + source = "hashicorp/helm" + version = "3.0.2" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.38.0" + } + kustomization = { + source = "kbst/kustomization" + version = "0.9.6" + } + } +} + + +data "kustomization" "cloudflare-kustomization" { + path = "${path.module}/kustomization" +} + +resource "kustomization_resource" "cloudflare" { + provider = kustomization + + for_each = data.kustomization.cloudflare-kustomization.ids + + manifest = data.kustomization.cloudflare-kustomization.manifests[each.key] +} + +resource "kubectl_manifest" "cloudflare-api-token" { + yaml_body = templatefile("${path.module}/secret.yaml", { + cloudflare_api_token = var.cloudflare_api_token + }) +} + +resource "kubectl_manifest" "cloudflare-tunnel" { + yaml_body = templatefile("${path.module}/cluster-tunnel.yaml", { + cloudflare_tunnel_name = var.cloudflare_tunnel_name + cloudflare_email = var.cloudflare_email + cloudflare_domain = var.cloudflare_domain + cloudflare_account_id = var.cloudflare_account_id + }) + + depends_on = [kustomization_resource.cloudflare] +} \ No newline at end of file diff --git a/tofu/modules/cloudflare/secret.yaml b/tofu/modules/cloudflare/secret.yaml new file mode 100644 index 0000000..c910a20 --- /dev/null +++ b/tofu/modules/cloudflare/secret.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cloudflare-secrets + namespace: cloudflare-operator-system +type: Opaque +stringData: + CLOUDFLARE_API_TOKEN: "${cloudflare_api_token}" \ No newline at end of file diff --git a/tofu/modules/cloudflare/variables.tf b/tofu/modules/cloudflare/variables.tf new file mode 100644 index 0000000..83022d6 --- /dev/null +++ b/tofu/modules/cloudflare/variables.tf @@ -0,0 +1,30 @@ +variable "cloudflare_api_token" { + type = string + description = "Cloudflare API token" + sensitive = true + nullable = false +} + +variable "cloudflare_tunnel_name" { + type = string + description = "Cloudflare Tunnel Name" + default = "tofu-tunnel" + nullable = false +} +variable "cloudflare_email" { + type = string + description = "Cloudflare Email" + nullable = false +} + +variable "cloudflare_domain" { + type = string + description = "Cloudflare Domain" + nullable = false +} + +variable "cloudflare_account_id" { + type = string + description = "Cloudflare Account ID" + nullable = false +} diff --git a/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml b/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml index 6285ebc..f23ea6c 100644 --- a/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml +++ b/tofu/modules/maxscale/charts/maxscale-helm/Chart.yaml @@ -1,4 +1,4 @@ apiVersion: v2 name: maxscale-helm -version: 0.1.9 +version: 1.0.2 description: Helm chart for MaxScale related Kubernetes manifests diff --git a/tofu/modules/maxscale/charts/maxscale-helm/templates/maxscale-ui.yaml b/tofu/modules/maxscale/charts/maxscale-helm/templates/maxscale-ui.yaml new file mode 100644 index 0000000..2f336ff --- /dev/null +++ b/tofu/modules/maxscale/charts/maxscale-helm/templates/maxscale-ui.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.cfargotunnel.com/v1alpha1 +kind: TunnelBinding +metadata: + name: maxscale-tunnel-binding + namespace: mariadb-operator +subjects: + - name: mariadb-repl-maxscale + spec: + target: https://mariadb-repl-maxscale-internal.mariadb-operator.svc.cluster.local:8989 + fqdn: maxscale.{{ .Values.base_domain }} + noTlsVerify: true +tunnelRef: + kind: ClusterTunnel + name: cluster-tunnel \ No newline at end of file diff --git a/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-config-map.yaml b/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-config-map.yaml index a9444fc..d5e84d4 100644 --- a/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-config-map.yaml +++ b/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin-config-map.yaml @@ -10,23 +10,23 @@ data: echo " /* Maximum number of databases displayed on one page */ - $cfg['MaxDbList'] = 300; - $cfg['MaxNavigationItems'] = 300; + \$cfg['MaxDbList'] = 300; + \$cfg['MaxNavigationItems'] = 300; /* Additional servers */ - $servers = [ + \$servers = [ {{- range $i, $e := until (int (3)) }} 'mariadb-repl-{{ $i }}', {{- end }} ]; - foreach ($servers as $server) { - $i++; + foreach (\$servers as \$server) { + \$i++; /* Authentication type */ - $cfg['Servers'][$i]['auth_type'] = 'cookie'; + \$cfg['Servers'][\$i]['auth_type'] = 'cookie'; /* Server parameters */ - $cfg['Servers'][$i]['host'] = $server; - $cfg['Servers'][$i]['port'] = '3306'; - $cfg['Servers'][$i]['compress'] = false; - $cfg['Servers'][$i]['AllowNoPassword'] = false; + \$cfg['Servers'][\$i]['host'] = \$server; + \$cfg['Servers'][\$i]['port'] = '3306'; + \$cfg['Servers'][\$i]['compress'] = false; + \$cfg['Servers'][\$i]['AllowNoPassword'] = false; } " >> /opt/bitnami/phpmyadmin/config.inc.php {{- end }} diff --git a/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin.yaml b/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin.yaml new file mode 100644 index 0000000..a1abbc1 --- /dev/null +++ b/tofu/modules/maxscale/charts/maxscale-helm/templates/phpmyadmin.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.cfargotunnel.com/v1alpha1 +kind: TunnelBinding +metadata: + name: phpmyadmin-tunnel-binding + namespace: mariadb-operator +subjects: + - name: mariadb-repl-maxscale + spec: + target: http://phpmyadmin.mariadb-operator.svc.cluster.local:8080 + fqdn: mysql.{{ .Values.base_domain }} + noTlsVerify: true +tunnelRef: + kind: ClusterTunnel + name: cluster-tunnel \ No newline at end of file diff --git a/tofu/modules/maxscale/charts/maxscale-helm/values.yaml b/tofu/modules/maxscale/charts/maxscale-helm/values.yaml index dbc024d..f7a4a90 100644 --- a/tofu/modules/maxscale/charts/maxscale-helm/values.yaml +++ b/tofu/modules/maxscale/charts/maxscale-helm/values.yaml @@ -13,3 +13,5 @@ metallb: phpmyadmin: enabled: true + +base_domain: example.com diff --git a/tofu/modules/maxscale/main.tf b/tofu/modules/maxscale/main.tf index d396177..6356195 100644 --- a/tofu/modules/maxscale/main.tf +++ b/tofu/modules/maxscale/main.tf @@ -58,7 +58,7 @@ resource "helm_release" "mariadb-operator" { resource "helm_release" "maxscale_helm" { name = "maxscale-helm" chart = "${path.module}/charts/maxscale-helm" - version = "0.1.9" + version = "1.0.2" depends_on = [ helm_release.mariadb-operator-crds, kubectl_manifest.secrets ] timeout = 3600 @@ -70,5 +70,6 @@ resource "helm_release" "maxscale_helm" { { name = "metallb.primary_ip", value = var.primary_ip }, { name = "metallb.secondary_ip", value = var.secondary_ip }, { name = "phpmyadmin.enabled", value = tostring(var.phpmyadmin_enabled) }, + { name = "base_domain", value = var.cloudflare_domain } ] } diff --git a/tofu/modules/maxscale/variables.tf b/tofu/modules/maxscale/variables.tf index 797cceb..fb8c724 100644 --- a/tofu/modules/maxscale/variables.tf +++ b/tofu/modules/maxscale/variables.tf @@ -50,3 +50,9 @@ variable "mariadb_user_password" { type = string sensitive = true } + +variable "cloudflare_domain" { + type = string + default = "Base cloudflare domain, e.g. example.com" + nullable = false +} \ No newline at end of file diff --git a/tofu/terraform.tfvars.example b/tofu/terraform.tfvars.example index 1933452..9e61b03 100644 --- a/tofu/terraform.tfvars.example +++ b/tofu/terraform.tfvars.example @@ -20,3 +20,10 @@ metallb_secondary_ip = "10.80.0.131" # phpMyAdmin toggle phpmyadmin_enabled = true +cloudflare_account_id = "CHANGE_ME" +cloudflare_api_token = "CHANGE_ME" +cloudflare_email = "CHANGE_ME" +cloudflare_tunnel_name = "CHANGE_ME" +cloudflare_domain = "CHANGE_ME" + + diff --git a/tofu/variables.tf b/tofu/variables.tf index d079f48..f3a2400 100644 --- a/tofu/variables.tf +++ b/tofu/variables.tf @@ -63,3 +63,41 @@ variable "mariadb_user_password" { sensitive = true nullable = false } + +variable "cloudflare_api_token" { + type = string + description = "Cloudflare API token" + sensitive = true + nullable = false +} + +variable "cloudflare_tunnel_name" { + type = string + description = "Cloudflare Tunnel Name" + default = "cluster-tunnel" + nullable = false +} +variable "cloudflare_email" { + type = string + description = "Cloudflare Email" + nullable = false +} + +variable "cloudflare_domain" { + type = string + description = "Cloudflare Domain" + nullable = false +} + +variable "cloudflare_account_id" { + type = string + description = "Cloudflare Account ID" + nullable = false +} + +variable "argocd_admin_password" { + type = string + nullable = false + sensitive = true + description = "ArgoCD admin password" +} \ No newline at end of file