From 618ca06093f4bf541218a2b103e9b9027f3bbe9b Mon Sep 17 00:00:00 2001 From: Matthew Baggett <matthew@baggett.me> Date: Tue, 26 Nov 2024 10:50:53 +0100 Subject: [PATCH] Treafik woz 'ere --- docker/network/inputs.tf | 12 ++ docker/network/locals.tf | 7 +- docker/network/network.tf | 15 ++- docker/network/outputs.tf | 6 + docker/network/subnet.tf | 14 ++ docker/network/terraform.tf | 4 + docker/service/{image.tf => image_mirror.tf} | 8 +- docker/service/inputs.tf | 49 ++++++- docker/service/labels.tf | 27 ++++ docker/service/locals.tf | 13 +- docker/service/networks.tf | 4 + docker/service/service.tf | 40 ++++-- docker/socket-proxy/inputs.tf | 5 +- docker/socket-proxy/output.tf | 6 + docker/socket-proxy/socket-proxy.tf | 33 ++--- docker/volume/output.tf | 3 + .../forgejo/actions-runner/forgejo-runner.tf | 4 +- products/frigate/frigate.tf | 55 ++++++++ products/frigate/inputs.tf | 82 ++++++++++++ products/frigate/labels.tf | 24 ++++ products/frigate/output.tf | 3 + products/frigate/terraform.tf | 11 ++ products/gitea/actions-runner/gitea-runner.tf | 4 +- products/github/actions-runner/gha-runner.tf | 4 +- products/homeassistant/homeassistant.tf | 13 ++ products/homeassistant/inputs.tf | 35 +++++ products/homeassistant/outputs.tf | 3 + products/homeassistant/terraform.tf | 15 +++ products/minio/inputs.tf | 24 ++++ products/minio/minio.tf | 64 ++++++++++ products/minio/outputs.tf | 3 + products/minio/terraform.tf | 15 +++ products/mitmproxy/inputs.tf | 25 ++++ products/mitmproxy/mitmproxy.tf | 30 +++++ products/mitmproxy/outputs.tf | 3 + products/mitmproxy/terraform.tf | 11 ++ products/pgbackweb/inputs.tf | 4 + products/pgbackweb/pgbackweb.tf | 1 + products/portainer/ui/inputs.tf | 41 ++++-- products/portainer/ui/output.tf | 2 +- products/portainer/ui/ui.tf | 120 ++++-------------- products/smokeping/inputs.tf | 18 +++ products/smokeping/outputs.tf | 3 + products/smokeping/smokeping.tf | 15 +++ products/smokeping/terraform.tf | 16 +++ products/statping/inputs.tf | 8 ++ products/statping/nginx.tf | 24 ---- products/statping/outputs.tf | 2 +- products/statping/statping.tf | 3 +- products/traefik/docker-socket-proxy.tf | 5 + products/traefik/hello.tf | 14 ++ products/traefik/inputs.tf | 43 +++++++ products/traefik/network.tf | 6 + products/traefik/outputs.tf | 6 + products/traefik/terraform.tf | 13 ++ products/traefik/traefik.tf | 69 ++++++++++ products/watchtower/outputs.tf | 3 + 57 files changed, 891 insertions(+), 199 deletions(-) create mode 100644 docker/network/subnet.tf rename docker/service/{image.tf => image_mirror.tf} (76%) create mode 100644 docker/service/labels.tf create mode 100644 docker/service/networks.tf create mode 100644 docker/socket-proxy/output.tf create mode 100644 products/frigate/frigate.tf create mode 100644 products/frigate/inputs.tf create mode 100644 products/frigate/labels.tf create mode 100644 products/frigate/output.tf create mode 100644 products/frigate/terraform.tf create mode 100644 products/homeassistant/homeassistant.tf create mode 100644 products/homeassistant/inputs.tf create mode 100644 products/homeassistant/outputs.tf create mode 100644 products/homeassistant/terraform.tf create mode 100644 products/minio/inputs.tf create mode 100644 products/minio/minio.tf create mode 100644 products/minio/outputs.tf create mode 100644 products/minio/terraform.tf create mode 100644 products/mitmproxy/inputs.tf create mode 100644 products/mitmproxy/mitmproxy.tf create mode 100644 products/mitmproxy/outputs.tf create mode 100644 products/mitmproxy/terraform.tf create mode 100644 products/smokeping/inputs.tf create mode 100644 products/smokeping/outputs.tf create mode 100644 products/smokeping/smokeping.tf create mode 100644 products/smokeping/terraform.tf delete mode 100644 products/statping/nginx.tf create mode 100644 products/traefik/docker-socket-proxy.tf create mode 100644 products/traefik/hello.tf create mode 100644 products/traefik/inputs.tf create mode 100644 products/traefik/network.tf create mode 100644 products/traefik/outputs.tf create mode 100644 products/traefik/terraform.tf create mode 100644 products/traefik/traefik.tf create mode 100644 products/watchtower/outputs.tf diff --git a/docker/network/inputs.tf b/docker/network/inputs.tf index ab10d20..793178d 100644 --- a/docker/network/inputs.tf +++ b/docker/network/inputs.tf @@ -7,3 +7,15 @@ variable "labels" { default = {} description = "A map of labels to apply to the service" } + +variable "network_name" { + type = string + description = "Override the automatically selected name of the network" + default = null +} + +variable "subnet" { + type = string + description = "The subnet to use for the network." + default = null //"172.16.0.0/16" +} diff --git a/docker/network/locals.tf b/docker/network/locals.tf index 6f5cd5f..b01e7fb 100644 --- a/docker/network/locals.tf +++ b/docker/network/locals.tf @@ -1,9 +1,12 @@ locals { - network_name = var.stack_name + // Concat up the network name + network_name = var.network_name != null ? "${var.stack_name}-${var.network_name}" : var.stack_name + + // Attach labels labels = merge(var.labels, { "com.docker.stack.namespace" = var.stack_name "ooo.grey.network.stack" = var.stack_name "ooo.grey.network.name" = local.network_name - #"ooo.grey.network.created" = timestamp() + "ooo.grey.network.subnet" = local.subnet }) } \ No newline at end of file diff --git a/docker/network/network.tf b/docker/network/network.tf index 7ef7ab9..f666b67 100644 --- a/docker/network/network.tf +++ b/docker/network/network.tf @@ -1,6 +1,13 @@ resource "docker_network" "instance" { - name = local.network_name - driver = "overlay" + name = local.network_name + driver = "overlay" + attachable = true + ipam_driver = "default" + ipam_config { + aux_address = {} + subnet = local.subnet + gateway = local.gateway + } # Attach labels dynamic "labels" { @@ -10,4 +17,8 @@ resource "docker_network" "instance" { value = labels.value } } + + lifecycle { + create_before_destroy = false + } } \ No newline at end of file diff --git a/docker/network/outputs.tf b/docker/network/outputs.tf index 70f01ea..23cb35c 100644 --- a/docker/network/outputs.tf +++ b/docker/network/outputs.tf @@ -1,3 +1,9 @@ output "network" { value = docker_network.instance.id +} +output "name" { + value = docker_network.instance.name +} +output "id" { + value = docker_network.instance.id } \ No newline at end of file diff --git a/docker/network/subnet.tf b/docker/network/subnet.tf new file mode 100644 index 0000000..531515b --- /dev/null +++ b/docker/network/subnet.tf @@ -0,0 +1,14 @@ +resource "random_integer" "upper_mid_byte" { + min = 18 + max = 31 +} +resource "random_integer" "lower_mid_byte" { + min = 10 + max = 254 +} +locals { + // Generate a subnet + subnet = var.subnet != null ? var.subnet : "172.${random_integer.upper_mid_byte.result}.${random_integer.lower_mid_byte.result}.0/24" + // Calculate the gateway from the subnet + gateway = cidrhost(local.subnet, 1) +} \ No newline at end of file diff --git a/docker/network/terraform.tf b/docker/network/terraform.tf index 0814b57..e7ca61a 100644 --- a/docker/network/terraform.tf +++ b/docker/network/terraform.tf @@ -5,6 +5,10 @@ terraform { source = "kreuzwerker/docker" version = "~>3.0" } + random = { + source = "hashicorp/random" + version = "~>3.3" + } } } diff --git a/docker/service/image.tf b/docker/service/image_mirror.tf similarity index 76% rename from docker/service/image.tf rename to docker/service/image_mirror.tf index 318a6dd..c61675b 100644 --- a/docker/service/image.tf +++ b/docker/service/image_mirror.tf @@ -1,17 +1,19 @@ +/* resource "docker_image" "mirror" { - count = var.mirror != null ? 1 : 0 + count = local.enable_mirror ? 1 : 0 name = data.docker_registry_image.image.name pull_triggers = [data.docker_registry_image.image.sha256_digest] force_remove = false } resource "docker_tag" "mirror" { - count = var.mirror != null ? 1 : 0 + count = local.enable_mirror ? 1 : 0 source_image = docker_image.mirror[0].name target_image = var.mirror } resource "docker_registry_image" "mirror" { - count = var.mirror != null ? 1 : 0 + count = local.enable_mirror ? 1 : 0 depends_on = [docker_tag.mirror[0]] name = docker_tag.mirror[0].target_image keep_remotely = true } +*/ \ No newline at end of file diff --git a/docker/service/inputs.tf b/docker/service/inputs.tf index 7ff3195..9e0b691 100644 --- a/docker/service/inputs.tf +++ b/docker/service/inputs.tf @@ -19,6 +19,11 @@ variable "restart_policy" { condition = var.restart_policy == "any" || var.restart_policy == "on-failure" || var.restart_policy == "none" } } +variable "restart_delay" { + type = string + default = "0s" + description = "The delay before restarting the service." +} variable "one_shot" { type = bool default = false @@ -53,9 +58,12 @@ variable "volumes" { description = "A map of volume names to create and mount. The key is the volume name, and the value is the mount point." } variable "remote_volumes" { - type = map(string) + type = map(object({ + id = string + driver = string + })) default = {} - description = "A map of remote volumes to mount into the container." + description = "A map of remote volumes to mount into the container. The key is the source, and the value is the target." } variable "mounts" { type = map(string) @@ -76,8 +84,8 @@ variable "ports" { default = [] description = "A map of port mappings to expose on the host. The key is the host port, and the value is the container port." validation { - error_message = "Host Ports must be between 1024 and 65535." - condition = alltrue([for port in var.ports : port.host >= 1024 && port.host <= 65535]) + error_message = "Host Ports must be between 1 and 65535." + condition = alltrue([for port in var.ports : port.host >= 1 && port.host <= 65535]) } validation { error_message = "Container Ports must be between 1 and 65535." @@ -105,6 +113,11 @@ variable "parallelism" { type = number description = "The number of instances to run." } +variable "parallelism_per_node" { + default = 0 + type = number + description = "The maximum number of instances to run per node. 0 means no limit." +} variable "update_waves" { default = 3 type = number @@ -144,4 +157,32 @@ variable "converge_timeout" { default = "2m" type = string description = "The timeout for the service to converge." +} +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number) + }) + description = "Whether to enable traefik for the service." +} +variable "limit_cpu" { + default = null + type = number + description = "The CPU limit for the service." +} +variable "limit_ram_mb" { + default = null + type = number + description = "The RAM limit for the service, measured in megabytes." +} +variable "reserved_cpu" { + default = null + type = number + description = "The CPU reservation for the service." +} +variable "reserved_ram_mb" { + default = null + type = number + description = "The RAM reservation for the service, measured in megabytes." } \ No newline at end of file diff --git a/docker/service/labels.tf b/docker/service/labels.tf new file mode 100644 index 0000000..688051c --- /dev/null +++ b/docker/service/labels.tf @@ -0,0 +1,27 @@ +locals { + # Define service labels en-masse + labels = merge(var.labels, { + "com.docker.stack.namespace" = var.stack_name + "com.docker.stack.image" = data.docker_registry_image.image.name + "ooo.grey.service.stack" = var.stack_name + "ooo.grey.service.name" = var.service_name + "ooo.grey.service.image" = data.docker_registry_image.image.name + "ooo.grey.service.image.digest" = data.docker_registry_image.image.sha256_digest + }, local.traefik_labels) + + # Calculate the traefik labels to use if enabled + traefik_labels = merge( + (var.traefik == null ? { + "traefik.enable" = "false" + } : { + "traefik.enable" = "true" + "traefik.http.routers.${local.service_name}.rule" = "Host(`${var.traefik.domain}`)" + "traefik.http.routers.${local.service_name}.entrypoints" = "websecure" + "traefik.http.routers.${local.service_name}.tls.certresolver" = "default" + }), + (try(var.traefik.port, null) == null ? {} : { + "traefik.http.services.${local.service_name}.loadbalancer.server.port" = var.traefik.port + }) + ) + +} \ No newline at end of file diff --git a/docker/service/locals.tf b/docker/service/locals.tf index f0bfe6e..c826ec9 100644 --- a/docker/service/locals.tf +++ b/docker/service/locals.tf @@ -5,16 +5,9 @@ locals { substr(var.service_name, 0, 63 - 1 - 20), ]) - # Define service labels en-masse - labels = merge(var.labels, { - "com.docker.stack.namespace" = var.stack_name - "com.docker.stack.image" = data.docker_registry_image.image.name - "ooo.grey.service.stack" = var.stack_name - "ooo.grey.service.name" = var.service_name - "ooo.grey.service.image" = data.docker_registry_image.image.name - "ooo.grey.service.image.digest" = data.docker_registry_image.image.sha256_digest - }) + enable_mirror = false // var.mirror != null # Calculate the docker image to use - image = var.mirror != null ? "${docker_registry_image.mirror[0].name}@${docker_registry_image.mirror[0].sha256_digest}" : "${data.docker_registry_image.image.name}@${data.docker_registry_image.image.sha256_digest}" + #image = local.enable_mirror ? "${docker_registry_image.mirror[0].name}@${docker_registry_image.mirror[0].sha256_digest}" : "${data.docker_registry_image.image.name}@${data.docker_registry_image.image.sha256_digest}" + image = "${data.docker_registry_image.image.name}@${data.docker_registry_image.image.sha256_digest}" } \ No newline at end of file diff --git a/docker/service/networks.tf b/docker/service/networks.tf new file mode 100644 index 0000000..1e1133d --- /dev/null +++ b/docker/service/networks.tf @@ -0,0 +1,4 @@ +data "docker_network" "networks" { + count = var.networks != null ? length(var.networks) : 0 + name = var.networks[count.index] +} \ No newline at end of file diff --git a/docker/service/service.tf b/docker/service/service.tf index 3bf63d1..6f64dc1 100644 --- a/docker/service/service.tf +++ b/docker/service/service.tf @@ -17,9 +17,10 @@ resource "docker_service" "instance" { dynamic "mounts" { for_each = var.volumes content { - target = mounts.value - source = docker_volume.volume[mounts.key].id - type = "volume" + source = docker_volume.volume[mounts.key].id + target = mounts.value + type = "volume" + read_only = false # Nice assumption bro. } } @@ -27,9 +28,10 @@ resource "docker_service" "instance" { dynamic "mounts" { for_each = var.remote_volumes content { - target = mounts.value - source = mounts.key - type = "volume" + source = mounts.value.id + target = mounts.key + type = "volume" + read_only = false # Nice assumption bro. } } @@ -37,8 +39,8 @@ resource "docker_service" "instance" { dynamic "mounts" { for_each = var.mounts content { - target = mounts.value source = mounts.key + target = mounts.value type = "bind" read_only = false # Nice assumption bro. } @@ -50,7 +52,7 @@ resource "docker_service" "instance" { content { config_id = module.config[configs.key].id config_name = module.config[configs.key].name - file_name = configs.value + file_name = configs.key } } @@ -83,27 +85,41 @@ resource "docker_service" "instance" { # Apply the networks dynamic "networks_advanced" { - for_each = var.networks + for_each = data.docker_network.networks content { - name = networks_advanced.value + name = networks_advanced.value.id } } # Apply restart policy restart_policy { condition = var.one_shot ? "none" : var.restart_policy - delay = "0s" + delay = var.restart_delay window = "0s" max_attempts = 0 } + # Apply the placement constraints placement { - constraints = var.placement_constraints + max_replicas = var.parallelism_per_node + constraints = var.placement_constraints platforms { architecture = var.processor_architecture os = var.operating_system } } + + # Apply the resource limits and reservations + resources { + limits { + memory_bytes = var.limit_ram_mb != null ? 1024 * 1024 * var.limit_ram_mb : 0 + nano_cpus = var.limit_cpu != null ? (1000000000 / 100) * var.limit_cpu : 0 + } + reservation { + memory_bytes = var.reserved_ram_mb != null ? 1024 * 1024 * var.reserved_ram_mb : 0 + nano_cpus = var.reserved_cpu != null ? (1000000000 / 100) * var.reserved_cpu : 0 + } + } } # Global deploy diff --git a/docker/socket-proxy/inputs.tf b/docker/socket-proxy/inputs.tf index 3feea56..9cb6f79 100644 --- a/docker/socket-proxy/inputs.tf +++ b/docker/socket-proxy/inputs.tf @@ -19,7 +19,8 @@ variable "service_name" { description = "The name of the service to create." } variable "placement_constraints" { - default = ["node.role == manager"] + default = [] type = list(string) description = "Docker Swarm placement constraints" -} \ No newline at end of file +} + diff --git a/docker/socket-proxy/output.tf b/docker/socket-proxy/output.tf new file mode 100644 index 0000000..3e730c4 --- /dev/null +++ b/docker/socket-proxy/output.tf @@ -0,0 +1,6 @@ +output "docker_service" { + value = module.service.docker_service +} +output "network" { + value = module.network.network +} \ No newline at end of file diff --git a/docker/socket-proxy/socket-proxy.tf b/docker/socket-proxy/socket-proxy.tf index 55f8680..e54a187 100644 --- a/docker/socket-proxy/socket-proxy.tf +++ b/docker/socket-proxy/socket-proxy.tf @@ -1,14 +1,17 @@ module "network" { - source = "../network" - name = "docker-socket-proxy" - stack_name = var.stack_name + source = "../network" + network_name = "docker-socket-proxy" + stack_name = var.stack_name } module "service" { - source = "../service" - image = "${var.docker_socket_proxy_image}:${var.docker_socket_proxy_version}" - command = ["/docker-entrypoint.sh", "sockd-username"] - stack_name = var.stack_name - service_name = var.service_name + source = "../service" + image = "${var.docker_socket_proxy_image}:${var.docker_socket_proxy_version}" + stack_name = var.stack_name + service_name = var.service_name + placement_constraints = concat(["node.role == manager"], var.placement_constraints) + global = true + networks = [module.network.network] + mounts = { "/var/run/docker.sock" = "/var/run/docker.sock" } environment_variables = { SWARM = 1 SERVICES = 1 @@ -17,18 +20,4 @@ module "service" { NODES = 1 NETWORKS = 1 } - placement_constraints = var.placement_constraints - global = true - networks = [module.network.network.id] - mounts = [ - { - target = "/var/run/docker.sock" - source = "/var/run/docker.sock" - read_only = false - type = "bind" - } - ] } -output "network" { - value = module.network.network -} \ No newline at end of file diff --git a/docker/volume/output.tf b/docker/volume/output.tf index 03d5415..91fe32f 100644 --- a/docker/volume/output.tf +++ b/docker/volume/output.tf @@ -1,3 +1,6 @@ output "source" { value = docker_volume.volume.id +} +output "volume" { + value = docker_volume.volume } \ No newline at end of file diff --git a/products/forgejo/actions-runner/forgejo-runner.tf b/products/forgejo/actions-runner/forgejo-runner.tf index f979346..eaeb8b0 100644 --- a/products/forgejo/actions-runner/forgejo-runner.tf +++ b/products/forgejo/actions-runner/forgejo-runner.tf @@ -13,9 +13,7 @@ module "forgejo_actions_runner" { forgejo_RUNNER_REGISTRATION_TOKEN = var.forgejo_token CONFIG_FILE = "/config.yaml" } - mounts = { - "/var/run/docker.sock" = "/var/run/docker.sock" - } + mounts = { "/var/run/docker.sock" = "/var/run/docker.sock" } configs = { forgejo-config = yamlencode({ name_prefix = ["forgejo-config", var.stack_name, var.service_name] diff --git a/products/frigate/frigate.tf b/products/frigate/frigate.tf new file mode 100644 index 0000000..0e3d811 --- /dev/null +++ b/products/frigate/frigate.tf @@ -0,0 +1,55 @@ +data "docker_registry_image" "frigate" { + name = "ghcr.io/blakeblackshear/frigate:stable" +} + +resource "docker_container" "frigate" { + image = "${data.docker_registry_image.frigate.name}@${data.docker_registry_image.frigate.sha256_digest}" + name = local.container_name + restart = "unless-stopped" + privileged = "true" + shm_size = var.shm_size_mb + network_mode = "bridge" + env = [ + "FRIGATE_RTSP_PASSWORD=${var.frigate_rtsp_password}" + ] + dynamic "devices" { + for_each = var.devices + content { + host_path = devices.value.host_path + container_path = devices.value.container_path + permissions = devices.value.permissions + } + } + dynamic "volumes" { + for_each = var.volumes + content { + container_path = volumes.value + host_path = volumes.key + read_only = false + } + } + dynamic "ports" { + for_each = var.ports + content { + internal = ports.value.container + external = ports.value.host + protocol = ports.value.protocol + } + } + dynamic "networks_advanced" { + for_each = var.networks + content { + name = networks_advanced.value + } + } + dynamic "labels" { + for_each = local.labels + content { + label = labels.key + value = labels.value + } + } + lifecycle { + create_before_destroy = false + } +} \ No newline at end of file diff --git a/products/frigate/inputs.tf b/products/frigate/inputs.tf new file mode 100644 index 0000000..e1cf1ce --- /dev/null +++ b/products/frigate/inputs.tf @@ -0,0 +1,82 @@ +variable "stack_name" { + type = string + description = "The name of the stack to deploy the service to." +} +variable "shm_size_mb" { + default = 256 + type = number + description = "The size of the shared memory segment in MB" +} +variable "networks" { + type = list(string) + default = [] + description = "A list of network names to attach the service to." +} +variable "frigate_rtsp_password" { + type = string + description = "The password to use for the RTSP streams" + default = "" +} +variable "devices" { + type = list(object({ + host_path = string + container_path = string + permissions = optional(string, "rwm") + })) + description = "The devices to mount into the container" +} +variable "volumes" { + type = map(string) + default = {} + description = "A map of volume names to create and mount. The key is the volume name, and the value is the mount point." +} + +variable "ports" { + type = list(object({ + host = number + container = number + protocol = optional(string, "tcp") + })) + default = [ + { + container = 5000 + host = 5000 + protocol = "tcp" + }, + { + container = 1935 + host = 1935 + protocol = "tcp" + }, + { + container = 8554 + host = 8554 + protocol = "tcp" + }, + { + container = 8555 + host = 8555 + protocol = "tcp" + }, + { + container = 8555 + host = 8555 + protocol = "udp" + } + ] +} + +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number, 5000) + }) + description = "Whether to enable traefik for the service." +} + +variable "labels" { + type = map(string) + default = {} + description = "A map of labels to apply to the service" +} diff --git a/products/frigate/labels.tf b/products/frigate/labels.tf new file mode 100644 index 0000000..2cf9289 --- /dev/null +++ b/products/frigate/labels.tf @@ -0,0 +1,24 @@ + +locals { + container_name = "frigate" + # Define service labels en-masse + labels = merge({ + "com.docker.stack.namespace" = var.stack_name + "com.docker.stack.image" = data.docker_registry_image.frigate.name + "ooo.grey.service.stack" = var.stack_name + "ooo.grey.service.name" = local.container_name + "ooo.grey.service.image" = data.docker_registry_image.frigate.name + "ooo.grey.service.image.digest" = data.docker_registry_image.frigate.sha256_digest + }, local.traefik_labels, var.labels) + + # Calculate the traefik labels to use if enabled + traefik_labels = var.traefik != null ? { + "traefik.enable" = "true" + "traefik.http.routers.${local.container_name}.rule" = "Host(`${var.traefik.domain}`)" + "traefik.http.routers.${local.container_name}.entrypoints" = "websecure" + "traefik.http.routers.${local.container_name}.tls.certresolver" = "default" + "traefik.http.services.${local.container_name}.loadbalancer.server.port" = 5000 + } : { + "traefik.enable" = "false" + } +} \ No newline at end of file diff --git a/products/frigate/output.tf b/products/frigate/output.tf new file mode 100644 index 0000000..3b5a914 --- /dev/null +++ b/products/frigate/output.tf @@ -0,0 +1,3 @@ +output "endpoint" { + value = try("https://${var.traefik.domain}", "unknown") +} \ No newline at end of file diff --git a/products/frigate/terraform.tf b/products/frigate/terraform.tf new file mode 100644 index 0000000..0814b57 --- /dev/null +++ b/products/frigate/terraform.tf @@ -0,0 +1,11 @@ +terraform { + required_version = "~> 1.6" + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + } +} + + diff --git a/products/gitea/actions-runner/gitea-runner.tf b/products/gitea/actions-runner/gitea-runner.tf index f472446..2a3004a 100644 --- a/products/gitea/actions-runner/gitea-runner.tf +++ b/products/gitea/actions-runner/gitea-runner.tf @@ -12,9 +12,7 @@ module "gitea_actions_runner" { GITEA_RUNNER_REGISTRATION_TOKEN = var.gitea_token CONFIG_FILE = "/config.yaml" } - mounts = { - "/var/run/docker.sock" = "/var/run/docker.sock" - } + mounts = { "/var/run/docker.sock" = "/var/run/docker.sock" } configs = { gitea-config = { name_prefix = ["gitea-config", var.stack_name, var.service_name] diff --git a/products/github/actions-runner/gha-runner.tf b/products/github/actions-runner/gha-runner.tf index 9ef6057..cec7c9f 100644 --- a/products/github/actions-runner/gha-runner.tf +++ b/products/github/actions-runner/gha-runner.tf @@ -15,7 +15,5 @@ module "github_actions_runner" { EPHEMERAL = true DISABLE_AUTO_UPDATE = "disable_updates" } - mounts = { - "/var/run/docker.sock" = "/var/run/docker.sock" - } + mounts = { "/var/run/docker.sock" = "/var/run/docker.sock" } } \ No newline at end of file diff --git a/products/homeassistant/homeassistant.tf b/products/homeassistant/homeassistant.tf new file mode 100644 index 0000000..0160e34 --- /dev/null +++ b/products/homeassistant/homeassistant.tf @@ -0,0 +1,13 @@ +module "homeassistant" { + source = "../../docker/service" + stack_name = var.stack_name + service_name = "homeassistant" + image = var.default_image + environment_variables = merge({ TZ = "Europe/London" }, var.environment_variables) + mounts = var.mounts + networks = var.networks + placement_constraints = var.placement_constraints + ports = [{ host = 8123, container = 8123 }] + traefik = var.traefik + converge_timeout = "5m" +} \ No newline at end of file diff --git a/products/homeassistant/inputs.tf b/products/homeassistant/inputs.tf new file mode 100644 index 0000000..e6df592 --- /dev/null +++ b/products/homeassistant/inputs.tf @@ -0,0 +1,35 @@ +variable "stack_name" { + default = "homeassistant" + type = string + description = "The name of the stack to create." +} + +variable "default_image" { + default = "ghcr.io/home-assistant/home-assistant:stable" + type = string + description = "The image to use for the homeassistant service" +} + +variable "environment_variables" { + type = map(string) + default = {} + description = "A map of environment variables to set in the container." +} +variable "mounts" { + type = map(string) + default = {} + description = "A map of host paths to container paths to mount. The key is the host path, and the value is the container path." +} +variable "networks" { + type = list(string) + default = [] + description = "A list of network names to attach the service to." +} +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} +variable "traefik" { + +} \ No newline at end of file diff --git a/products/homeassistant/outputs.tf b/products/homeassistant/outputs.tf new file mode 100644 index 0000000..69672b7 --- /dev/null +++ b/products/homeassistant/outputs.tf @@ -0,0 +1,3 @@ +output "docker_service" { + value = module.homeassistant.docker_service +} \ No newline at end of file diff --git a/products/homeassistant/terraform.tf b/products/homeassistant/terraform.tf new file mode 100644 index 0000000..4496f20 --- /dev/null +++ b/products/homeassistant/terraform.tf @@ -0,0 +1,15 @@ +terraform { + required_version = "~> 1.6" + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + } +} + + diff --git a/products/minio/inputs.tf b/products/minio/inputs.tf new file mode 100644 index 0000000..7dcd8a6 --- /dev/null +++ b/products/minio/inputs.tf @@ -0,0 +1,24 @@ +variable "stack_name" { + default = "mitmproxy" + type = string + description = "The name of the stack to create." +} +variable "networks" { + type = list(string) + default = [] + description = "A list of network names to attach the service to." +} + +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number) + }) + description = "Whether to enable traefik for the service." +} +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} diff --git a/products/minio/minio.tf b/products/minio/minio.tf new file mode 100644 index 0000000..bb09113 --- /dev/null +++ b/products/minio/minio.tf @@ -0,0 +1,64 @@ +resource "random_pet" "minio_admin_user" { + length = 2 + separator = "" +} +resource "random_password" "minio_admin_password" { + length = 32 + special = false +} +variable "domain" { + type = string + description = "The domain to use for the service." +} +variable "mounts" { + type = map(string) + default = {} + description = "A map of host paths to container paths to mount. The key is the host path, and the value is the container path." +} +module "minio" { + source = "../../docker/service" + stack_name = "minio" + service_name = "minio" + image = "quay.io/minio/minio:latest" + command = ["minio", "server", "/data", ] + environment_variables = { + MINIO_ADDRESS = "0.0.0.0:9000" + MINIO_CONSOLE_ADDRESS = "0.0.0.0:9001" + MINIO_ROOT_USER = random_pet.minio_admin_user.id + MINIO_ROOT_PASSWORD = random_password.minio_admin_password.result + MINIO_SERVER_URL = "https://s3.grey.ooo" + MINIO_BROWSER_REDIRECT_URL = "https://s3.grey.ooo/ui/" + MINIO_BROWSER_REDIRECT = true + MINIO_API_ROOT_ACCESS = "on" + } + mounts = var.mounts + networks = concat(["loadbalancer-traefik"], var.networks) + placement_constraints = var.placement_constraints + labels = { + "traefik.enable" = "true" + + // API redirect + "traefik.http.routers.minio_api.rule" = "Host(`${var.domain}`) && !PathPrefix(`/ui`)" + #"traefik.http.routers.minio_api.service" = "minio_api" + "traefik.http.routers.minio_api.entrypoints" = "websecure" + "traefik.http.routers.minio_api.tls.certresolver" = "default" + "traefik.http.services.minio_api.loadbalancer.server.port" = "9000" + + // UI redirect + "traefik.http.routers.minio_ui.rule" = "Host(`${var.domain}`) && PathPrefix(`/ui`)" + #"traefik.http.routers.minio_ui.service" = "minio_ui" + "traefik.http.routers.minio_ui.entrypoints" = "websecure" + "traefik.http.routers.minio_ui.tls.certresolver" = "default" + "traefik.http.services.minio_ui.loadbalancer.server.port" = "9001" + } +} + +output "minio" { + value = { + endpoint = "https://${var.domain}/ui/" + auth = { + username = module.minio.docker_service.task_spec[0].container_spec[0].env.MINIO_ROOT_USER + password = nonsensitive(module.minio.docker_service.task_spec[0].container_spec[0].env.MINIO_ROOT_PASSWORD) + } + } +} diff --git a/products/minio/outputs.tf b/products/minio/outputs.tf new file mode 100644 index 0000000..d5d704b --- /dev/null +++ b/products/minio/outputs.tf @@ -0,0 +1,3 @@ +output "docker_service" { + value = module.minio.docker_service +} \ No newline at end of file diff --git a/products/minio/terraform.tf b/products/minio/terraform.tf new file mode 100644 index 0000000..4496f20 --- /dev/null +++ b/products/minio/terraform.tf @@ -0,0 +1,15 @@ +terraform { + required_version = "~> 1.6" + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + } +} + + diff --git a/products/mitmproxy/inputs.tf b/products/mitmproxy/inputs.tf new file mode 100644 index 0000000..822565e --- /dev/null +++ b/products/mitmproxy/inputs.tf @@ -0,0 +1,25 @@ +variable "stack_name" { + default = "mitmproxy" + type = string + description = "The name of the stack to create." +} + +variable "networks" { + type = list(string) + default = [] + description = "A list of network names to attach the service to." +} + +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number, 8081) + }) + description = "Whether to enable traefik for the service." +} +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} diff --git a/products/mitmproxy/mitmproxy.tf b/products/mitmproxy/mitmproxy.tf new file mode 100644 index 0000000..fc660a2 --- /dev/null +++ b/products/mitmproxy/mitmproxy.tf @@ -0,0 +1,30 @@ +module "mitmproxy" { + source = "../../docker/service" + stack_name = var.stack_name + service_name = "mitmproxy" + networks = var.networks + image = "ghcr.io/benzine-framework/mitmproxy" + command = [ + "mitmweb", + "--web-host", "0.0.0.0", + "--web-port", "8081", + #"--listen-host", "0.0.0.0", + #"--listen-port", "8080", + #"--ssl-insecure", + ] + healthcheck = ["CMD-SHELL", " curl -I http://localhost:8081 || exit 1"] + placement_constraints = var.placement_constraints + traefik = var.traefik + ports = [ + { + protocol = "tcp" + container = 8080 + host = 4080 + }, + { + protocol = "tcp" + container = 8081 + host = 4081 + } + ] +} \ No newline at end of file diff --git a/products/mitmproxy/outputs.tf b/products/mitmproxy/outputs.tf new file mode 100644 index 0000000..613b9be --- /dev/null +++ b/products/mitmproxy/outputs.tf @@ -0,0 +1,3 @@ +output "docker_service" { + value = module.mitmproxy.docker_service +} \ No newline at end of file diff --git a/products/mitmproxy/terraform.tf b/products/mitmproxy/terraform.tf new file mode 100644 index 0000000..0814b57 --- /dev/null +++ b/products/mitmproxy/terraform.tf @@ -0,0 +1,11 @@ +terraform { + required_version = "~> 1.6" + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + } +} + + diff --git a/products/pgbackweb/inputs.tf b/products/pgbackweb/inputs.tf index 9e74469..0e059ba 100644 --- a/products/pgbackweb/inputs.tf +++ b/products/pgbackweb/inputs.tf @@ -26,4 +26,8 @@ variable "placement_constraints" { variable "networks" { type = list(string) default = [] +} +variable "domain" { + type = string + description = "The domain to use for the service's traefik configuration." } \ No newline at end of file diff --git a/products/pgbackweb/pgbackweb.tf b/products/pgbackweb/pgbackweb.tf index 3e0a6ee..3b18060 100644 --- a/products/pgbackweb/pgbackweb.tf +++ b/products/pgbackweb/pgbackweb.tf @@ -17,6 +17,7 @@ module "pgbackweb" { service_name = var.service_name networks = concat([module.network.network], var.networks) placement_constraints = var.placement_constraints + traefik = { domain = var.domain } } module "postgres" { source = "../postgres" diff --git a/products/portainer/ui/inputs.tf b/products/portainer/ui/inputs.tf index 2e5607c..066bb21 100644 --- a/products/portainer/ui/inputs.tf +++ b/products/portainer/ui/inputs.tf @@ -1,16 +1,33 @@ -variable "docker" { - type = object({ - name = string - stack_name = optional(string) - networks = list(object({ - name = string - id = string - })) - }) +variable "stack_name" { + default = "loadbalancer" + type = string + description = "The name of the stack to create." } -variable "portainer" { +variable "traefik" { + default = null type = object({ - version = string - logo = optional(string) + domain = string + port = optional(number, 9000) }) + description = "Whether to enable traefik for the service." } +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} + +variable "portainer_version" { + default = "sts" + type = string + description = "The version of the portainer image to use." +} +variable "portainer_logo" { + default = null + type = string + description = "The URL of the logo to use for the portainer service." +} +variable "should_mount_local_docker_socket" { + type = bool + default = false +} \ No newline at end of file diff --git a/products/portainer/ui/output.tf b/products/portainer/ui/output.tf index 7853309..e05921f 100644 --- a/products/portainer/ui/output.tf +++ b/products/portainer/ui/output.tf @@ -4,6 +4,6 @@ output "portainer" { username = "admin" # Sorry, this is hardcoded in the portainer image password = nonsensitive(random_password.password.result) } - service_name = docker_service.portainer.name + service_name = module.portainer.docker_service.name } } diff --git a/products/portainer/ui/ui.tf b/products/portainer/ui/ui.tf index b4e2fd5..1ee86dd 100644 --- a/products/portainer/ui/ui.tf +++ b/products/portainer/ui/ui.tf @@ -8,99 +8,31 @@ resource "htpasswd_password" "hash" { password = random_password.password.result salt = random_password.salt.result } -data "docker_registry_image" "portainer_app" { - name = "portainer/portainer-ce:${var.portainer.version}" +module "vol_portainer" { + source = "../../../docker/volume" + stack_name = var.stack_name + volume_name = "portainer" } -resource "docker_volume" "portainer" { - name = var.docker.name -} -resource "docker_service" "portainer" { - name = var.docker.name - mode { - replicated { - replicas = 1 - } - } - task_spec { - container_spec { - image = "${data.docker_registry_image.portainer_app.name}@${data.docker_registry_image.portainer_app.sha256_digest}" - command = [ - "/portainer", - //"--edge-compute", - "--logo", coalesce(var.portainer.logo), - "--admin-password", htpasswd_password.hash.bcrypt, - ] - #mounts { - # target = "/data" - # source = "/portainer" - # read_only = false - # type = "bind" - #} - mounts { - target = "/data" - source = docker_volume.portainer.name - type = "volume" - read_only = false - } - #mounts { - # target = "/var/run/docker.sock" - # source = "/var/run/docker.sock" - # read_only = false - # type = "bind" - #} - labels { - label = "com.docker.stack.namespace" - value = var.docker.stack_name - } - } - dynamic "networks_advanced" { - for_each = var.docker.networks - content { - name = networks_advanced.value.id - } - } - restart_policy { - condition = "on-failure" - delay = "3s" - max_attempts = 4 - window = "10s" - } - placement { - constraints = [ - "node.role == manager", - "node.platform.os == linux", - ] - } - } - #endpoint_spec { - # ports { - # target_port = 9000 - # publish_mode = "ingress" - # published_port = 9000 - # } - # ports { - # target_port = 8000 - # publish_mode = "ingress" - # published_port = 8000 - # } - #} - update_config { - # Portainer gets super fuckin' upset if you start a second instance while the first is holding the db lock - order = "stop-first" - } - - labels { - label = "com.docker.stack.namespace" - value = var.docker.stack_name - } - labels { - label = "com.docker.stack.image" - value = replace(data.docker_registry_image.portainer_app.name, "/:.*/", "") - } - lifecycle { - ignore_changes = [ - # MB: This is a hack because terraform keeps detecting a "change" in the placement->platform constraint that doesn't exist. - task_spec[0].placement[0].platforms - ] - } +module "portainer" { + source = "../../../docker/service" + stack_name = var.stack_name + service_name = "portainer" + image = "portainer/portainer-ce:${var.portainer_version}" + command = [ + "/portainer", + //"--edge-compute", + "--logo", coalesce(var.portainer_logo), + "--admin-password", htpasswd_password.hash.bcrypt, + ] + remote_volumes = { + "/data" = module.vol_portainer.volume + } + traefik = var.traefik + mounts = var.should_mount_local_docker_socket ? { "/var/run/docker.sock" = "/var/run/docker.sock" } : {} + networks = ["loadbalancer-traefik"] + start_first = false + placement_constraints = concat([ + "node.role == manager", + "node.platform.os == linux", + ], var.placement_constraints) } diff --git a/products/smokeping/inputs.tf b/products/smokeping/inputs.tf new file mode 100644 index 0000000..af0c2c2 --- /dev/null +++ b/products/smokeping/inputs.tf @@ -0,0 +1,18 @@ +variable "timezone" { + type = string + description = "The timezone to use for the service." + default = "Europe/London" +} +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number) + }) + description = "Whether to enable traefik for the service." +} +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} diff --git a/products/smokeping/outputs.tf b/products/smokeping/outputs.tf new file mode 100644 index 0000000..c594383 --- /dev/null +++ b/products/smokeping/outputs.tf @@ -0,0 +1,3 @@ +output "docker_service" { + value = module.smokeping.docker_service +} \ No newline at end of file diff --git a/products/smokeping/smokeping.tf b/products/smokeping/smokeping.tf new file mode 100644 index 0000000..fdefda9 --- /dev/null +++ b/products/smokeping/smokeping.tf @@ -0,0 +1,15 @@ +module "smokeping" { + source = "../../docker/service" + stack_name = "smokeping" + service_name = "smokeping" + image = "linuxserver/smokeping:latest" + volumes = { "smokeping" = "/data" } + environment_variables = { + PUID = 1000 + PGID = 1000 + TZ = var.timezone + } + traefik = var.traefik + networks = ["loadbalancer-traefik"] + placement_constraints = var.placement_constraints +} \ No newline at end of file diff --git a/products/smokeping/terraform.tf b/products/smokeping/terraform.tf new file mode 100644 index 0000000..263a49f --- /dev/null +++ b/products/smokeping/terraform.tf @@ -0,0 +1,16 @@ +terraform { + required_version = "~> 1.6" + + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + } +} + + diff --git a/products/statping/inputs.tf b/products/statping/inputs.tf index cec6296..65355e3 100644 --- a/products/statping/inputs.tf +++ b/products/statping/inputs.tf @@ -42,3 +42,11 @@ variable "extra_environment_variables" { default = {} description = "Extra environment variables to pass to the service." } +variable "traefik" { + default = null + type = object({ + domain = string + port = optional(number) + }) + description = "Whether to enable traefik for the service." +} \ No newline at end of file diff --git a/products/statping/nginx.tf b/products/statping/nginx.tf deleted file mode 100644 index 64ce7cd..0000000 --- a/products/statping/nginx.tf +++ /dev/null @@ -1,24 +0,0 @@ -variable "nginx_hostname" { - type = string - default = null -} -variable "acme_certificate" { - type = object({ - private_key_pem = string - certificate_pem = string - issuer_pem = string - }) - default = null -} -module "nginx_config" { - count = var.nginx_hostname != null ? 1 : 0 - source = "../nginx/site-available" - service_name = module.service.service_name - hostname = var.nginx_hostname - upstream_host = "${module.service.service_name}:8080" - config_prefix = module.service.service_name - certificate = var.acme_certificate -} -output "nginx_files" { - value = var.nginx_hostname != null ? module.nginx_config[0].files : [] -} \ No newline at end of file diff --git a/products/statping/outputs.tf b/products/statping/outputs.tf index 15a2e8f..26cad0e 100644 --- a/products/statping/outputs.tf +++ b/products/statping/outputs.tf @@ -7,7 +7,7 @@ output "statping" { port = module.postgres.ports[0] } statping = { - instance = var.nginx_hostname != null ? "https://${var.nginx_hostname}" : null + instance = try("https://${var.traefik.domain}", "unknown") } } } diff --git a/products/statping/statping.tf b/products/statping/statping.tf index dd7ce47..158eecf 100644 --- a/products/statping/statping.tf +++ b/products/statping/statping.tf @@ -15,7 +15,7 @@ module "service" { image = "${var.statping_image}:${var.statping_version}" stack_name = var.stack_name service_name = "statping" - networks = concat([module.network.network, ], var.networks) + networks = concat([module.network.network, "loadbalancer-traefik"], var.networks) environment_variables = merge({ VIRTUAL_HOST = "localhost" VIRTUAL_PORT = "8080" @@ -29,4 +29,5 @@ module "service" { }, var.extra_environment_variables) placement_constraints = var.placement_constraints dns_nameservers = var.dns_nameservers + traefik = var.traefik } diff --git a/products/traefik/docker-socket-proxy.tf b/products/traefik/docker-socket-proxy.tf new file mode 100644 index 0000000..b020b44 --- /dev/null +++ b/products/traefik/docker-socket-proxy.tf @@ -0,0 +1,5 @@ +module "docker_socket_proxy" { + source = "../../docker/socket-proxy" + stack_name = var.stack_name + placement_constraints = var.placement_constraints +} \ No newline at end of file diff --git a/products/traefik/hello.tf b/products/traefik/hello.tf new file mode 100644 index 0000000..6769ff8 --- /dev/null +++ b/products/traefik/hello.tf @@ -0,0 +1,14 @@ +module "traefik_hello" { + count = var.hello_service_domain != null ? 1 : 0 + source = "../../docker/service" + stack_name = var.stack_name + service_name = "hello" + image = "traefik/whoami" + parallelism = 3 + placement_constraints = var.placement_constraints + networks = [module.traefik_network.network, ] + traefik = { + domain = var.hello_service_domain + port = 80 + } +} \ No newline at end of file diff --git a/products/traefik/inputs.tf b/products/traefik/inputs.tf new file mode 100644 index 0000000..d77744f --- /dev/null +++ b/products/traefik/inputs.tf @@ -0,0 +1,43 @@ +variable "stack_name" { + default = "loadbalancer" + type = string + description = "The name of the stack to create." +} + +variable "placement_constraints" { + default = [] + type = list(string) + description = "Docker Swarm placement constraints" +} +variable "acme_use_staging" { + type = bool + default = false + description = "Whether to use the Let's Encrypt staging server." +} +variable "acme_email" { + description = "The email address to use for the ACME certificate." + type = string +} +variable "traefik_service_domain" { + type = string + default = null +} +variable "hello_service_domain" { + type = string + default = null +} +variable "log_level" { + type = string + default = "INFO" + description = "The log level to use for traefik." +} +variable "access_log" { + type = bool + default = false + description = "Whether to enable access logging." +} +variable "redirect_to_ssl" { + type = bool + default = true + description = "Whether to redirect HTTP to HTTPS." +} \ No newline at end of file diff --git a/products/traefik/network.tf b/products/traefik/network.tf new file mode 100644 index 0000000..064fa64 --- /dev/null +++ b/products/traefik/network.tf @@ -0,0 +1,6 @@ +module "traefik_network" { + source = "../../docker/network" + stack_name = var.stack_name + network_name = "traefik" + subnet = "172.16.0.0/22" +} diff --git a/products/traefik/outputs.tf b/products/traefik/outputs.tf new file mode 100644 index 0000000..06c1a95 --- /dev/null +++ b/products/traefik/outputs.tf @@ -0,0 +1,6 @@ +output "docker_service" { + value = module.traefik.docker_service +} +output "docker_network" { + value = module.traefik_network +} \ No newline at end of file diff --git a/products/traefik/terraform.tf b/products/traefik/terraform.tf new file mode 100644 index 0000000..ed365fe --- /dev/null +++ b/products/traefik/terraform.tf @@ -0,0 +1,13 @@ +terraform { + required_version = "~> 1.6" + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~>3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + } +} \ No newline at end of file diff --git a/products/traefik/traefik.tf b/products/traefik/traefik.tf new file mode 100644 index 0000000..101e6a8 --- /dev/null +++ b/products/traefik/traefik.tf @@ -0,0 +1,69 @@ +module "traefik_certs_volume" { + source = "../../docker/volume" + stack_name = var.stack_name + volume_name = "traefik_certs" +} +module "traefik" { + source = "../../docker/service" + depends_on = [module.docker_socket_proxy] + stack_name = var.stack_name + service_name = "traefik" + image = "traefik:v3.2" + networks = [module.traefik_network.network, module.docker_socket_proxy.network, ] + mounts = { "/goliath/letsencrypt" = "/certs" } + placement_constraints = var.placement_constraints + converge_enable = false // @todo add healthcheck + command = [ + "/usr/local/bin/traefik", + "--api.insecure=true", + "--api.dashboard=true", + "--log.level=${var.log_level}", + "--accesslog=${var.access_log ? "true" : "false"}", + "--ping=true", + + # Confirm Docker Provider + "--providers.docker=true", + "--providers.docker.exposedbydefault=false", + "--providers.docker.network=${module.traefik_network.name}", + "--providers.docker.endpoint=http://${module.docker_socket_proxy.docker_service.name}:2375", + + # Confirm Swarm Provider + "--providers.swarm=true", + "--providers.swarm.exposedByDefault=false", + "--providers.swarm.network=${module.traefik_network.name}", + "--providers.swarm.endpoint=http://${module.docker_socket_proxy.docker_service.name}:2375", + + # Configure HTTP and redirect to HTTPS + "--entrypoints.web.address=:80", + + # Configure HTTPS + "--entrypoints.websecure.address=:443", + var.redirect_to_ssl ? "--entrypoints.web.http.redirections.entrypoint.to=websecure" : "", + var.redirect_to_ssl ? "--entrypoints.web.http.redirections.entrypoint.scheme=https" : "", + + # Configure the acme provider + "--certificatesresolvers.default.acme.tlschallenge=true", + var.acme_use_staging ? "--certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" : "", + "--certificatesresolvers.default.acme.email=${var.acme_email}", + "--certificatesresolvers.default.acme.storage=/certs/acme.json", + ] + traefik = var.traefik_service_domain != null ? { + domain = var.traefik_service_domain + port = 8080 + } : null + ports = [ + { + host = 80 + container = 80 + }, + { + host = 443 + container = 443 + }, + { + host = 8080 + container = 8080 + }, + ] + +} diff --git a/products/watchtower/outputs.tf b/products/watchtower/outputs.tf new file mode 100644 index 0000000..366cd6f --- /dev/null +++ b/products/watchtower/outputs.tf @@ -0,0 +1,3 @@ +output "docker_service" { + value = module.watchtower.docker_service +}