diff --git a/application/application.tf b/application/application.tf index 04b7bb7..1827782 100644 --- a/application/application.tf +++ b/application/application.tf @@ -1,5 +1,5 @@ locals { - app_name = var.component == var.instance ? var.instance : format("%s-%s", var.component, var.instance) + app_name = (var.component == var.instance || var.component=="") ? var.instance : format("%s-%s", var.component, var.instance) main_group = format("app-%s", local.app_name) } data "authentik_group" "akadmin" { diff --git a/backup/backup.tf b/backup/backup.tf new file mode 100644 index 0000000..c526b8f --- /dev/null +++ b/backup/backup.tf @@ -0,0 +1,42 @@ +locals { + app_slug = (var.component == var.instance || var.component=="") ? var.instance : format("%s-%s", var.component, var.instance) +} + +resource "kubectl_manifest" "backup_schedule" { + count = var.backups.enable ? 1 : 0 + yaml_body = <<-EOF + apiVersion: k8up.io/v1 + kind: Schedule + metadata: + name: "${var.app_slug}-backup" + namespace: "${var.namespace}" + labels: ${jsonencode(local.labels)} + spec: + backend: + repoPasswordSecretRef: + key: "${var.backups.restic_key}" + name: "${var.backups.secret_name}" + s3: + accessKeyIDSecretRef: + key: "${var.backups.key_id_key}" + name: "${var.backups.secret_name}" + bucket: "${var.app_slug}-${var.namespace}" + endpoint: "${var.backups.endpoint}/restic" + secretAccessKeySecretRef: + key: "${var.backups.secret_key}" + name: "${var.backups.secret-name}" + backup: + schedule: "${var.backups.schedule.backup}" + failedJobsHistoryLimit: 2 + successfulJobsHistoryLimit: 2 + check: + schedule: "${var.backups.schedule.check}" + prune: + retention: + keepDaily: ${var.backups.retention.keepDaily} + keepMonthly: ${var.backups.retention.keepMonthly} + keepWeekly: ${var.backups.retention.keepWeekly} + keepYearly: ${var.backups.retention.keepYearly} + schedule: "${var.backups.schedule.prune}" + EOF +} diff --git a/backup/outputs.tf b/backup/outputs.tf new file mode 100644 index 0000000..e15fe36 --- /dev/null +++ b/backup/outputs.tf @@ -0,0 +1,3 @@ +output "schedule_name" { + value = kubectl_manifest.backup_schedule.name +} diff --git a/backup/providers.tf b/backup/providers.tf new file mode 100644 index 0000000..45aaada --- /dev/null +++ b/backup/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "~> 1.14.0" + } + } +} diff --git a/backup/variables.tf b/backup/variables.tf new file mode 100644 index 0000000..ae57590 --- /dev/null +++ b/backup/variables.tf @@ -0,0 +1,55 @@ +variable "component" { + type = string +} +variable "instance" { + type = string +} +variable "namespace" { + type = string +} +variable "labels" { + type = map(string) +} +variable "backups" { + default = { + "enable" = false + "endpoint" = "" + "key_id_key" = "s3-id" + "restic_key" = "bck-password" + "retention" = { + "keepDaily" = 14 + "keepMonthly" = 12 + "keepWeekly" = 6 + "keepYearly" = 12 + } + "schedule" = { + "backup" = "30 3 * * *" + "check" = "30 5 * * 1" + "db" = "30 3 * * *" + "prune" = "30 1 * * 0" + } + "secret_key" = "s3-secret" + "secret_name" = "backup-settings" + "use_barman" = false + } + type = object({ + enable = optional(bool), + endpoint = optional(string), + key_id_key = optional(string), + restic_key = optional(string), + retention = optional(object({ + keepDaily = optional(number), + keepMonthly = optional(number), + keepWeekly = optional(number), + keepYearly = optional(number) + })), + schedule = optional(object({ + backup = optional(string), + check = optional(string), + prune = optional(string) + })), + secret_key = optional(string), + secret_name = optional(string), + use_barman = optional(bool) + }) +} \ No newline at end of file diff --git a/postgresql/outputs.tf b/postgresql/outputs.tf new file mode 100644 index 0000000..d4d88df --- /dev/null +++ b/postgresql/outputs.tf @@ -0,0 +1,3 @@ +output "host" { + value = "${var.app_slug}-redis.${var.namespace}.svc" +} diff --git a/postgresql/postgresql.tf b/postgresql/postgresql.tf new file mode 100644 index 0000000..63341dc --- /dev/null +++ b/postgresql/postgresql.tf @@ -0,0 +1,87 @@ +locals { + app_slug = (var.component == var.instance || var.component=="") ? var.instance : format("%s-%s", var.component, var.instance) + pg-labels = merge(local.labels, { + "app.kubernetes.io/component" = "pg" + }) + pool-labels = merge(local.labels, { + "app.kubernetes.io/component" = "pg-pool" + }) +} + +resource "kubectl_manifest" "prj_pg" { + yaml_body = join("", concat([<<-EOF + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + metadata: + name: "${local.app_slug}-pg" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + annotations: + "k8up.io/backupcommand": "pg_dump -U postgres -d ${var.component} --clean" + "k8up.io/file-extension": ".sql" + spec: + instances: ${var.replicas} + imageName: "${var.images.postgresql.registry}/${var.images.postgresql.repository}:${var.images.postgresql.tag}" + storage: + size: "${var.storage.size}" + bootstrap: + initdb: + database: "${var.component}" + owner: "${var.component}" + monitoring: + enablePodMonitor: true + EOF + ], var.backups.enable&&var.backups.use_barman?[<<-EOF + backup: + barmanObjectStore: + destinationPath: "s3://${var.app_slug}-${var.namespace}/" + endpointURL: "${var.backups.endpoint}/barman" + s3Credentials: + accessKeyId: + name: "${var.backups.secret_name}" + key: "${var.backups.key_id_key}" + secretAccessKey: + name: "${var.backups.secret_name}" + key: "${var.backups.secret_key}" + EOF + ]:[""])) +} + +resource "kubectl_manifest" "prj_pg_backup" { + count = var.backups.enable ? 1:0 + yaml_body = <<-EOF + apiVersion: postgresql.cnpg.io/v1 + kind: ScheduledBackup + metadata: + name: "${local.app_slug}-pg" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + spec: + schedule: "${var.backups.schedule.db}" + backupOwnerReference: self + cluster: + name: "${local.app_slug}-pg" + EOF +} + +resource "kubectl_manifest" "prj_pg_pool" { + depends_on = [kubectl_manifest.prj_pg] + yaml_body = <<-EOF + apiVersion: postgresql.cnpg.io/v1 + kind: Pooler + metadata: + name: "${local.app_slug}-pool" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pool-labels)} + spec: + cluster: + name: "${local.app_slug}-pg" + instances: 1 + type: rw + pgbouncer: + poolMode: session + parameters: + max_client_conn: "1000" + default_pool_size: "10" + EOF +} diff --git a/postgresql/providers.tf b/postgresql/providers.tf new file mode 100644 index 0000000..45aaada --- /dev/null +++ b/postgresql/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "~> 1.14.0" + } + } +} diff --git a/postgresql/variables.tf b/postgresql/variables.tf new file mode 100644 index 0000000..51ef389 --- /dev/null +++ b/postgresql/variables.tf @@ -0,0 +1,63 @@ +variable "component" { + type = string +} +variable "instance" { + type = string +} +variable "namespace" { + type = string +} +variable "labels" { + type = map(string) +} + +variable "backups" { + default = { + "enable" = false + "endpoint" = "" + "key_id_key" = "s3-id" + "restic_key" = "bck-password" + "schedule" = { + "db" = "30 3 * * *" + } + "secret_key" = "s3-secret" + "secret_name" = "backup-settings" + "use_barman" = false + } + type = object({ + enable = optional(bool), + endpoint = optional(string), + key_id_key = optional(string), + restic_key = optional(string), + schedule = optional(object({ + db = optional(string), + })), + secret_key = optional(string), + secret_name = optional(string), + use_barman = optional(bool) + }) +} +variable "images" { + type = object({ + postgresql = optional(object({registry = optional(string), repository = optional(string), tag = optional(number)})), + }) + default = { + "postgresql" = { + "registry" = "ghcr.io" + "repository" = "cloudnative-pg/postgresql" + "tag" = 15.3 + } + } +} +variable "replicas" { + type = number + default = 1 +} +variable "storage" { + type = object({ + size = optional(string) + }) + default = { + "size" = "5Gi" + } +} \ No newline at end of file diff --git a/pvc/outputs.tf b/pvc/outputs.tf new file mode 100644 index 0000000..233cef2 --- /dev/null +++ b/pvc/outputs.tf @@ -0,0 +1,3 @@ +output "name" { + value = local.app_slug +} diff --git a/pvc/providers.tf b/pvc/providers.tf new file mode 100644 index 0000000..45aaada --- /dev/null +++ b/pvc/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "~> 1.14.0" + } + } +} diff --git a/pvc/pvc.tf b/pvc/pvc.tf new file mode 100644 index 0000000..485bb3d --- /dev/null +++ b/pvc/pvc.tf @@ -0,0 +1,27 @@ +locals { + app_slug = (var.component == var.instance || var.component=="") ? var.instance : format("%s-%s", var.component, var.instance) + pvc_spec = merge({ + "accessModes" = [var.storage.accessMode] + "volumeMode" = var.storage.type + "resources" = { + "requests" = { + "storage" = "${var.storage.size}" + } + } + }, var.storage.volume.class != "" ?{ + "storageClassName" = var.storage.class + }:{}) +} +resource "kubectl_manifest" "pvc" { + yaml_body = <<-EOF + apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: ${local.app_slug} + namespace: "${var.namespace}" + annotations: + k8up.io/backup: "true" + labels: ${jsonencode(local.labels)} + spec: ${jsonencode(local.pvc_spec)} + EOF +} diff --git a/pvc/variables.tf b/pvc/variables.tf new file mode 100644 index 0000000..1e495fa --- /dev/null +++ b/pvc/variables.tf @@ -0,0 +1,26 @@ +variable "component" { + type = string +} +variable "instance" { + type = string +} +variable "namespace" { + type = string +} +variable "labels" { + type = map(string) +} +variable "storage" { + type = object({ + accessMode = optional(string), + class = optional(string), + size = optional(string), + type = optional(string) + }) + default = { + "accessMode" = "ReadWriteOnce" + "class" = "" + "size" = "10Gi" + "type" = "Filesystem" + } +} \ No newline at end of file diff --git a/redis/outputs.tf b/redis/outputs.tf new file mode 100644 index 0000000..d4d88df --- /dev/null +++ b/redis/outputs.tf @@ -0,0 +1,3 @@ +output "host" { + value = "${var.app_slug}-redis.${var.namespace}.svc" +} diff --git a/redis/providers.tf b/redis/providers.tf new file mode 100644 index 0000000..45aaada --- /dev/null +++ b/redis/providers.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubectl = { + source = "gavinbunney/kubectl" + version = "~> 1.14.0" + } + } +} diff --git a/redis/redis.tf b/redis/redis.tf new file mode 100644 index 0000000..429a131 --- /dev/null +++ b/redis/redis.tf @@ -0,0 +1,41 @@ +locals { + app_slug = (var.component == var.instance || var.component=="") ? var.instance : format("%s-%s", var.component, var.instance) + redis-labels = merge(local.labels, { + "app.kubernetes.io/component" = "redis" + }) + cfg = merge({ + "image" = "${var.images.redis.registry}/${var.images.redis.repository}:${var.images.redis.tag}" + "imagePullPolicy" = "${var.images.redis.pullPolicy}" + },lookup(var.password, "enabled", false)?{ + redisSecret = { + name = lookup(var.password, "name", var.component) + key = lookup(var.password, "key", "redis-password") + } + }:{}) +} + +resource "kubectl_manifest" "redis" { + yaml_body = <<-EOF + apiVersion: "redis.redis.opstreelabs.in/v1beta1" + kind: "Redis" + metadata: + name: "${local.app_slug}-redis" + namespace: "${var.namespace}" + labels: ${jsonencode(local.redis-labels)} + spec: + kubernetesConfig: ${jsonencode(cfg)} + storage: + volumeClaimTemplate: + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: "${var.storage.size}" + redisExporter: + enabled: ${var.exporter.enabled} + image: "${var.images.redis_exporter.registry}/${var.images.redis_exporter.repository}:${var.images.redis_exporter.tag}" + securityContext: + runAsUser: 1000 + fsGroup: 1000 + EOF +} diff --git a/redis/variables.tf b/redis/variables.tf new file mode 100644 index 0000000..589b3f3 --- /dev/null +++ b/redis/variables.tf @@ -0,0 +1,65 @@ +variable "component" { + type = string +} +variable "instance" { + type = string +} +variable "namespace" { + type = string +} +variable "labels" { + type = map(string) +} +variable "annotations" { + type = map(string) + default = {} +} + +variable "images" { + type = object({ + redis = optional(object({pullPolicy = optional(string), registry = optional(string), repository = optional(string), tag = optional(string)})), + redis_exporter = optional(object({pullPolicy = optional(string), registry = optional(string), repository = optional(string), tag = optional(string)})) + }) + default = { + "redis" = { + "pullPolicy" = "IfNotPresent" + "registry" = "quay.io" + "repository" = "opstree/redis" + "tag" = "v7.0.12" + } + "redis_exporter" = { + "pullPolicy" = "IfNotPresent" + "registry" = "quay.io" + "repository" = "opstree/redis-exporter" + "tag" = "v1.44.0" + } + } +} +variable "exporter" { + type = object({ + enabled = optional(bool) + }) + default = { + "enabled" = true + } +} +variable "password" { + type = object({ + enabled = optional(bool), + name = optional(string), + key = optional(string) + }) + default = { + "enabled" = false + "name" = "" + "key" = "" + } +} +variable "storage" { + type = object({ + size = optional(string) + }) + default = { + "size" = "2Gi" + } +} \ No newline at end of file