diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..c7814c2 --- /dev/null +++ b/main.tf @@ -0,0 +1,293 @@ +terraform { + required_providers { + authentik = { + source = "goauthentik/authentik" + version = "2022.8.1" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "2.13.1" + } + postgresql = { + source = "cyrilgdn/postgresql" + version = "1.17.1" + } + minio = { + source = "aminueza/minio" + version = "1.10.0" + } + } +} + +locals { + match_labels = merge({ + "app.kubernetes.io/instance" = "woodpecker" + }, var.match_labels) + + server_match_labels = merge({ + "app.kubernetes.io/name" = "woodpecker-server" + }, local.match_labels) + server_labels = merge(local.server_match_labels, { + "app.kubernetes.io/version" = "v0.15.6" + }, var.server_labels) + + agent_match_labels = merge({ + "app.kubernetes.io/name" = "woodpecker-agent" + }, local.match_labels) + agent_labels = merge(local.agent_match_labels, { + "app.kubernetes.io/version" = "v0.15.6" + }, var.agent_labels) +} + +resource "kubernetes_service_account" "woodpecker_server" { + metadata { + name = "woodpecker-server" + namespace = var.namespace + labels = local.server_labels + } +} + +resource "kubernetes_deployment" "woodpecker_server" { + metadata { + name = "woodpecker-server" + namespace = var.namespace + labels = local.server_labels + } + spec { + replicas = 1 + selector { + match_labels = local.server_match_labels + } + template { + metadata { + labels = local.server_labels + annotations = { + "ravianand.me/config-hash" = sha1(jsonencode(merge( + kubernetes_config_map.woodpecker_server.data, + kubernetes_secret.woodpecker.data + ))) + } + } + spec { + service_account_name = kubernetes_service_account.woodpecker_server.metadata.0.name + container { + image = var.image_registry == "" ? "${var.server_image_repository}:${var.server_image_tag}" : "${var.image_registry}/${var.server_image_repository}:${var.server_image_tag}" + name = "woodpecker" + env_from { + config_map_ref { + name = kubernetes_config_map.woodpecker_server.metadata.0.name + } + } + env { + name = "WOODPECKER_BACKEND" + value = "docker" + } + env { + name = "WOODPECKER_AGENT_SECRET" + value_from { + secret_key_ref { + name = kubernetes_secret.woodpecker.metadata.0.name + key = "woodpecker-agent-secret" + } + } + } + env { + name = "WOODPECKER_GITEA_SECRET" + value_from { + secret_key_ref { + name = kubernetes_secret.woodpecker.metadata.0.name + key = "gitea-secret" + optional = true + } + } + } + env { + name = "WOODPECKER_DATABASE_DATASOURCE" + value_from { + secret_key_ref { + name = kubernetes_secret.woodpecker.metadata.0.name + key = "database-url" + } + } + } + port { + name = "http" + container_port = 8000 + protocol = "TCP" + } + liveness_probe { + http_get { + path = "/healthz" + port = 8000 + } + } + readiness_probe { + http_get { + path = "/healthz" + port = 8000 + } + } + resources {} + } + } + } + } +} + +resource "kubernetes_service" "woodpecker_server" { + metadata { + name = "woodpecker-server" + namespace = var.namespace + labels = local.server_labels + } + spec { + selector = local.server_match_labels + type = "ClusterIP" + port { + port = 80 + name = "http" + target_port = 8000 + } + port { + port = 9000 + name = "grpc" + target_port = 9000 + } + } +} + +resource "kubernetes_service_account" "woodpecker_agent" { + count = var.woodpecker_agent_replicas == 0 ? 0 : 1 + metadata { + name = "woodpecker-agent" + namespace = var.namespace + labels = local.agent_labels + } +} + +resource "kubernetes_deployment" "woodpecker_agent" { + count = var.woodpecker_agent_replicas == 0 ? 0 : 1 + metadata { + name = "woodpecker-agent" + namespace = var.namespace + labels = local.agent_labels + } + spec { + replicas = var.woodpecker_agent_replicas + selector { + match_labels = local.agent_match_labels + } + template { + metadata { + labels = local.agent_labels + annotations = { + "ravianand.me/config-hash" = sha1(jsonencode(merge( + kubernetes_secret.woodpecker.data + ))) + } + } + spec { + service_account_name = kubernetes_service_account.woodpecker_agent.0.metadata.0.name + container { + image = var.image_registry == "" ? "${var.agent_image_repository}:${var.agent_image_tag}" : "${var.image_registry}/${var.agent_image_repository}:${var.agent_image_tag}" + name = "woodpecker-agent" + security_context {} + env { + name = "WOODPECKER_SERVER" + value = "${kubernetes_service.woodpecker_server.metadata.0.name}.${var.namespace}:9000" + } + env { + name = "WOODPECKER_AGENT_SECRET" + value_from { + secret_key_ref { + name = kubernetes_secret.woodpecker.metadata.0.name + key = "woodpecker-agent-secret" + } + } + } + port { + container_port = 3000 + name = "http" + protocol = "TCP" + } + resources { + requests = { + cpu = "250m" + memory = "250Mi" + } + limits = { + cpu = 2 + memory = "2Gi" + } + } + volume_mount { + mount_path = "/var/run" + name = "sock-dir" + } + } + container { + image = "docker:20.10.12-dind" + name = "dind" + env { + name = "DOCKER_DRIVER" + value = "overlay2" + } + resources { + requests = { + cpu = "250m" + memory = "250Mi" + } + limits = { + cpu = 1 + memory = "2Gi" + } + } + security_context { + privileged = true + } + volume_mount { + mount_path = "/var/run" + name = "sock-dir" + } + } + volume { + name = "sock-dir" + empty_dir {} + } + } + } + } +} + +resource "random_id" "woodpecker_agent_secret_key" { + byte_length = 32 +} + +resource "kubernetes_secret" "woodpecker" { + metadata { + name = "woodpecker" + namespace = var.namespace + } + data = { + "database-url" = var.woodpecker_database_datasource + "woodpecker-agent-secret" = random_id.woodpecker_agent_secret_key.hex + "gitea-secret" = var.woodpecker_gitea_secret + } +} + +resource "kubernetes_config_map" "woodpecker_server" { + metadata { + name = "woodpecker-server-env" + namespace = var.namespace + } + + data = { + WOODPECKER_ADMIN = var.woodpecker_admin + WOODPECKER_HOST = var.woodpecker_host + WOODPECKER_OPEN = var.woodpecker_open + WOODPECKER_GITEA = var.woodpecker_gitea + WOODPECKER_GITEA_URL = var.woodpecker_gitea_url + WOODPECKER_GITEA_CLIENT = var.woodpecker_gitea_client + WOODPECKER_DATABASE_DRIVER = var.woodpecker_database_driver + } +} diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..77528a8 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,14 @@ +output "service_name" { + description = "Service name for Woodpecker server deployment" + value = kubernetes_service.woodpecker_server.metadata.0.name +} + +output "service_http_port" { + description = "HTTP port exposed by the service" + value = kubernetes_service.woodpecker_server.spec.0.port.0.name +} + +output "service_grpc_port" { + description = "GRPC port exposed by the service" + value = kubernetes_service.woodpecker_server.spec.0.port.1.name +} diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..1efb110 --- /dev/null +++ b/variables.tf @@ -0,0 +1,111 @@ +variable "namespace" { + description = "Namespace to deploy workloads and configuration" + type = string + default = "default" +} + +variable "match_labels" { + description = "Match labels to add to the MariaDB deployment, will be merged with labels" + type = map(any) + default = {} +} + +variable "server_labels" { + description = "Labels to add to the Woodpecker server deployment" + type = map(any) + default = {} +} + +variable "agent_labels" { + description = "Labels to add to the Woodpecker agent deployment" + type = map(any) + default = {} +} + +variable "image_registry" { + description = "Image registry, e.g. gcr.io, docker.io" + type = string + default = "" +} + +variable "server_image_repository" { + description = "Image to start for the server" + type = string + default = "woodpeckerci/woodpecker-server" +} + +variable "server_image_tag" { + description = "Image tag to for the server" + type = string + default = "v0.15.6" +} + +variable "agent_image_repository" { + description = "Image to start for the agent" + type = string + default = "woodpeckerci/woodpecker-agent" +} + +variable "agent_image_tag" { + description = "Image tag to use for the agent" + type = string + default = "v0.15.6" +} + +variable "woodpecker_agent_replicas" { + description = "Number of agent replicas to deploy, setting to zero will disable agent deployment" + type = number + default = 2 +} + +variable "woodpecker_host" { + description = "Woodpecker host in :// format" + type = string +} + +variable "woodpecker_admin" { + description = "Comma-delimited list of Woodpecker admin users" + type = string +} + +variable "woodpecker_open" { + description = "Open Woodpecker registrations" + type = bool + default = true +} + +variable "woodpecker_gitea" { + description = "Enable Woodpecker Gitea integration" + type = bool + default = false +} + +variable "woodpecker_gitea_url" { + description = "Gitea URL" + type = string + default = "" +} + +variable "woodpecker_gitea_client" { + description = "Gitea client ID" + type = string + default = "" +} + +variable "woodpecker_gitea_secret" { + description = "Gitea client secret" + type = string + default = "" +} + +variable "woodpecker_database_driver" { + description = "Woodpecker database driver" + type = string + default = "postgres" +} + +variable "woodpecker_database_datasource" { + description = "Database URL" + type = string + default = "" +}