diff --git a/modules/kubernetes/.terraform.lock.hcl b/modules/kubernetes/.terraform.lock.hcl
index 9687444..7301670 100644
--- a/modules/kubernetes/.terraform.lock.hcl
+++ b/modules/kubernetes/.terraform.lock.hcl
@@ -39,3 +39,23 @@ provider "registry.terraform.io/hashicorp/kubernetes" {
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}
+
+provider "registry.terraform.io/hashicorp/random" {
+ version = "3.6.2"
+ constraints = ">= 3.6.2, < 4.0.0"
+ hashes = [
+ "h1:wmG0QFjQ2OfyPy6BB7mQ57WtoZZGGV07uAPQeDmIrAE=",
+ "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec",
+ "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53",
+ "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114",
+ "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad",
+ "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b",
+ "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916",
+ "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150",
+ "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544",
+ "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7",
+ "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af",
+ ]
+}
diff --git a/modules/kubernetes/README.md b/modules/kubernetes/README.md
index 23abc58..b340de0 100644
--- a/modules/kubernetes/README.md
+++ b/modules/kubernetes/README.md
@@ -8,6 +8,7 @@
| [terraform](#requirement\_terraform) | >= 1.0.0 |
| [helm](#requirement\_helm) | >= 2.14.0, < 3.0.0 |
| [kubernetes](#requirement\_kubernetes) | >= 2.31.0, < 3.0.0 |
+| [random](#requirement\_random) | >= 3.6.2, < 4.0.0 |
## Providers
@@ -15,6 +16,7 @@
|------|---------|
| [helm](#provider\_helm) | 2.14.0 |
| [kubernetes](#provider\_kubernetes) | 2.31.0 |
+| [random](#provider\_random) | 3.6.2 |
## Modules
@@ -25,25 +27,35 @@ No modules.
| Name | Type |
|------|------|
| [helm_release.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
+| [helm_release.external_dns](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [helm_release.hcloud_ccm](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [helm_release.hcloud_csi](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
+| [helm_release.ingress_nginx](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [kubernetes_annotations.hcloud_ccm](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/annotations) | resource |
| [kubernetes_namespace.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource |
+| [kubernetes_namespace_v1.external_dns](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace_v1) | resource |
| [kubernetes_secret_v1.cluster_autoscaler](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
+| [kubernetes_secret_v1.external_dns](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
| [kubernetes_secret_v1.hcloud](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
+| [random_integer.ingress_load_balancer_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/integer) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
+| [cloudflare\_api\_token](#input\_cloudflare\_api\_token) | Cloudflare API token | `string` | n/a | yes |
| [cluster\_autoscaler\_version](#input\_cluster\_autoscaler\_version) | Version of Cluster Autoscaler to use - defaults to latest | `string` | `null` | no |
+| [external\_dns\_version](#input\_external\_dns\_version) | Version of External DNS to install - defaults to latest | `string` | `null` | no |
| [hcloud\_network\_name](#input\_hcloud\_network\_name) | Hetzner network name | `string` | n/a | yes |
| [hcloud\_token](#input\_hcloud\_token) | Hetzner API token | `string` | n/a | yes |
| [hetzner\_cloud\_config\_manager\_version](#input\_hetzner\_cloud\_config\_manager\_version) | Version of the HCloud CCM to use - defaults to latest | `string` | `null` | no |
| [hetzner\_csi\_driver\_version](#input\_hetzner\_csi\_driver\_version) | Tag of the CSI driver to use - defaults to latest | `string` | `null` | no |
+| [ingress\_nginx\_version](#input\_ingress\_nginx\_version) | Version of Ingress Nginx to install - defaults to latest | `string` | `null` | no |
| [k3s\_cluster\_cidr](#input\_k3s\_cluster\_cidr) | CIDR used for the k3s cluster | `string` | `"10.244.0.0/16"` | no |
| [kube\_context](#input\_kube\_context) | Kubernetes context to use | `string` | `"default"` | no |
| [kubeconfig](#input\_kubeconfig) | Kubeconfig for the cluster | `string` | n/a | yes |
+| [load\_balancer\_region](#input\_load\_balancer\_region) | Region to use for the load balancer | `string` | n/a | yes |
+| [load\_balancer\_type](#input\_load\_balancer\_type) | Type of load balancer to use | `string` | `"lb11"` | no |
| [worker\_pools](#input\_worker\_pools) | Cluster autoscaler configuration |
list(object({
cloud_init = string
firewall_id = string
image = string
labels = list(object({
key = string
value = string
}))
network_id = string
pool = object({
instanceType = string
minSize = number
maxSize = number
name = string
region = string
})
ssh_key_id = string
taints = list(object({
key = string
value = string
effect = string
}))
}))
| `[]` | no |
## Outputs
diff --git a/modules/kubernetes/ingress.tf b/modules/kubernetes/ingress.tf
new file mode 100644
index 0000000..c160f8b
--- /dev/null
+++ b/modules/kubernetes/ingress.tf
@@ -0,0 +1,102 @@
+# Copyright 2024 Simon Emms
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+resource "kubernetes_namespace_v1" "external_dns" {
+ metadata {
+ name = "external-dns"
+ }
+
+ wait_for_default_service_account = true
+}
+
+resource "kubernetes_secret_v1" "external_dns" {
+ metadata {
+ name = "cloudflare"
+ namespace = kubernetes_namespace_v1.external_dns.metadata[0].name
+ }
+
+ data = {
+ cloudflare_api_token = var.cloudflare_api_token
+ }
+}
+
+resource "helm_release" "external_dns" {
+ chart = "oci://registry-1.docker.io/bitnamicharts/external-dns"
+ name = "external-dns"
+ atomic = true
+ cleanup_on_fail = true
+ namespace = kubernetes_namespace_v1.external_dns.metadata[0].name
+ reset_values = true
+ version = var.external_dns_version
+ wait = true
+
+ set {
+ name = "provider"
+ value = "cloudflare"
+ }
+
+ set {
+ name = "cloudflare.secretName"
+ value = kubernetes_secret_v1.external_dns.metadata[0].name
+ }
+
+ set {
+ name = "podAnnotations.secret"
+ value = sha512(yamlencode(kubernetes_secret_v1.external_dns.data))
+ }
+}
+
+resource "random_integer" "ingress_load_balancer_id" {
+ min = 1000
+ max = 9999
+}
+
+resource "helm_release" "ingress_nginx" {
+ chart = "ingress-nginx"
+ name = "ingress-nginx"
+ atomic = true
+ cleanup_on_fail = true
+ create_namespace = true
+ namespace = "ingress-nginx"
+ repository = "https://kubernetes.github.io/ingress-nginx"
+ reset_values = true
+ version = var.ingress_nginx_version
+ wait = true
+
+ dynamic "set" {
+ for_each = {
+ "load-balancer.hetzner.cloud/name" = "k3s-${random_integer.ingress_load_balancer_id.result}"
+ "load-balancer.hetzner.cloud/network-zone" = var.load_balancer_region
+ "load-balancer.hetzner.cloud/type" = var.load_balancer_type
+ "load-balancer.hetzner.cloud/disable-private-ingress" = "true"
+ "load-balancer.hetzner.cloud/use-private-ip" = "true"
+ "load-balancer.hetzner.cloud/uses-proxyprotocol" = "true"
+ }
+ content {
+ name = "controller.service.annotations.${replace(set.key, ".", "\\.")}"
+ value = set.value
+ }
+ }
+
+ set {
+ name = "controller.config.use-proxy-protocol"
+ value = "true"
+ }
+
+ # Depend upon the HCloud CCM to allow the load balancer to be deleted on destroy
+ depends_on = [
+ helm_release.hcloud_ccm,
+ helm_release.hcloud_csi,
+ ]
+}
diff --git a/modules/kubernetes/terraform.tf b/modules/kubernetes/terraform.tf
index 9909e16..f84da4b 100644
--- a/modules/kubernetes/terraform.tf
+++ b/modules/kubernetes/terraform.tf
@@ -23,6 +23,10 @@ terraform {
source = "hashicorp/kubernetes"
version = ">= 2.31.0, < 3.0.0"
}
+ random = {
+ source = "hashicorp/random"
+ version = ">= 3.6.2, < 4.0.0"
+ }
}
}
diff --git a/modules/kubernetes/variables.tf b/modules/kubernetes/variables.tf
index 8d3b860..4d641be 100644
--- a/modules/kubernetes/variables.tf
+++ b/modules/kubernetes/variables.tf
@@ -18,6 +18,18 @@ variable "cluster_autoscaler_version" {
default = null
}
+variable "cloudflare_api_token" {
+ type = string
+ description = "Cloudflare API token"
+ sensitive = true
+}
+
+variable "external_dns_version" {
+ type = string
+ description = "Version of External DNS to install - defaults to latest"
+ default = null
+}
+
variable "hcloud_network_name" {
type = string
description = "Hetzner network name"
@@ -41,6 +53,12 @@ variable "hetzner_csi_driver_version" {
default = null
}
+variable "ingress_nginx_version" {
+ type = string
+ description = "Version of Ingress Nginx to install - defaults to latest"
+ default = null
+}
+
variable "k3s_cluster_cidr" {
type = string
description = "CIDR used for the k3s cluster"
@@ -59,6 +77,17 @@ variable "kube_context" {
default = "default"
}
+variable "load_balancer_region" {
+ type = string
+ description = "Region to use for the load balancer"
+}
+
+variable "load_balancer_type" {
+ type = string
+ description = "Type of load balancer to use"
+ default = "lb11"
+}
+
variable "worker_pools" {
type = list(object({
cloud_init = string
diff --git a/stacks/dev/kubernetes/.terraform.lock.hcl b/stacks/dev/kubernetes/.terraform.lock.hcl
index 9687444..7301670 100644
--- a/stacks/dev/kubernetes/.terraform.lock.hcl
+++ b/stacks/dev/kubernetes/.terraform.lock.hcl
@@ -39,3 +39,23 @@ provider "registry.terraform.io/hashicorp/kubernetes" {
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
]
}
+
+provider "registry.terraform.io/hashicorp/random" {
+ version = "3.6.2"
+ constraints = ">= 3.6.2, < 4.0.0"
+ hashes = [
+ "h1:wmG0QFjQ2OfyPy6BB7mQ57WtoZZGGV07uAPQeDmIrAE=",
+ "zh:0ef01a4f81147b32c1bea3429974d4d104bbc4be2ba3cfa667031a8183ef88ec",
+ "zh:1bcd2d8161e89e39886119965ef0f37fcce2da9c1aca34263dd3002ba05fcb53",
+ "zh:37c75d15e9514556a5f4ed02e1548aaa95c0ecd6ff9af1119ac905144c70c114",
+ "zh:4210550a767226976bc7e57d988b9ce48f4411fa8a60cd74a6b246baf7589dad",
+ "zh:562007382520cd4baa7320f35e1370ffe84e46ed4e2071fdc7e4b1a9b1f8ae9b",
+ "zh:5efb9da90f665e43f22c2e13e0ce48e86cae2d960aaf1abf721b497f32025916",
+ "zh:6f71257a6b1218d02a573fc9bff0657410404fb2ef23bc66ae8cd968f98d5ff6",
+ "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
+ "zh:9647e18f221380a85f2f0ab387c68fdafd58af6193a932417299cdcae4710150",
+ "zh:bb6297ce412c3c2fa9fec726114e5e0508dd2638cad6a0cb433194930c97a544",
+ "zh:f83e925ed73ff8a5ef6e3608ad9225baa5376446349572c2449c0c0b3cf184b7",
+ "zh:fbef0781cb64de76b1df1ca11078aecba7800d82fd4a956302734999cfd9a4af",
+ ]
+}
diff --git a/stacks/dev/kubernetes/terragrunt.hcl b/stacks/dev/kubernetes/terragrunt.hcl
index ea3989e..ddb51e6 100644
--- a/stacks/dev/kubernetes/terragrunt.hcl
+++ b/stacks/dev/kubernetes/terragrunt.hcl
@@ -27,13 +27,15 @@ dependency "hetzner" {
hcloud_network_name = "some-network-name"
k3s_cluster_cidr = "some-cluster-cidr"
kubeconfig = "some-kubeconfig"
+ region = "some-region"
worker_pools = []
}
}
inputs = {
- hcloud_network_name = dependency.hetzner.outputs.hcloud_network_name
- k3s_cluster_cidr = dependency.hetzner.outputs.k3s_cluster_cidr
- kubeconfig = dependency.hetzner.outputs.kubeconfig
- worker_pools = dependency.hetzner.outputs.worker_pools
+ hcloud_network_name = dependency.hetzner.outputs.hcloud_network_name
+ k3s_cluster_cidr = dependency.hetzner.outputs.k3s_cluster_cidr
+ kubeconfig = dependency.hetzner.outputs.kubeconfig
+ load_balancer_region = dependency.hetzner.outputs.region
+ worker_pools = dependency.hetzner.outputs.worker_pools
}