From 007c98d2820ea3098414f51afef857f80f4e0505 Mon Sep 17 00:00:00 2001 From: Matthew Baggett Date: Fri, 21 Jun 2024 17:52:01 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + docker-portainer-edge-agent/agent.tf | 99 +++++++++++++++++++++ docker-portainer-edge-agent/input.tf | 26 ++++++ docker-portainer-edge-agent/terraform.tf | 18 ++++ docker-portainer-ui/inputs.tf | 16 ++++ docker-portainer-ui/output.tf | 9 ++ docker-portainer-ui/terraform.tf | 18 ++++ docker-portainer-ui/ui.tf | 106 +++++++++++++++++++++++ 8 files changed, 293 insertions(+) create mode 100644 .gitignore create mode 100644 docker-portainer-edge-agent/agent.tf create mode 100644 docker-portainer-edge-agent/input.tf create mode 100644 docker-portainer-edge-agent/terraform.tf create mode 100644 docker-portainer-ui/inputs.tf create mode 100644 docker-portainer-ui/output.tf create mode 100644 docker-portainer-ui/terraform.tf create mode 100644 docker-portainer-ui/ui.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09c56d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea diff --git a/docker-portainer-edge-agent/agent.tf b/docker-portainer-edge-agent/agent.tf new file mode 100644 index 0000000..e80aca9 --- /dev/null +++ b/docker-portainer-edge-agent/agent.tf @@ -0,0 +1,99 @@ +//resource "random_uuid" "edge_id" {} +data "docker_registry_image" "portainer_agent" { + name = "portainer/agent:${var.portainer.version}" +} +resource "docker_network" "portainer_edge_network" { + name = "portainer_edge_network" + driver = "overlay" + labels { + label = "com.docker.stack.namespace" + value = var.docker.stack_name + } + lifecycle { + ignore_changes = [ + ipam_config, + ] + } +} +resource "docker_service" "portainer_edge_agent" { + name = var.docker.name_agent + mode { + global = true + } + task_spec { + container_spec { + image = "${data.docker_registry_image.portainer_agent.name}@${data.docker_registry_image.portainer_agent.sha256_digest}" + command = concat(["./agent"], var.debug ? ["--log-level", "DEBUG"] : []) + env = { + AGENT_CLUSTER_ADDR = "tasks.${var.docker.name_agent}" + EDGE = 1 + EDGE_ID = var.edge_id + #EDGE_KEY = local.edge_key + EDGE_KEY = var.edge_key + EDGE_INSECURE_POLL = 1 + } + mounts { + target = "/var/run/docker.sock" + source = "/var/run/docker.sock" + read_only = false + type = "bind" + } + mounts { + target = "/var/lib/docker/volumes" + source = "/var/lib/docker/volumes" + read_only = false + type = "bind" + } + mounts { + target = "/host" + source = "/" + read_only = false + type = "bind" + } + // MB:Might need to add a portainer_agent_data volume (not bind) here mounting int /data + labels { + label = "com.docker.stack.namespace" + value = var.docker.stack_name + } + } + networks_advanced { + name = docker_network.portainer_edge_network.id + } + restart_policy { + condition = "on-failure" + delay = "0s" + #max_attempts = -1 + window = "10s" + } + placement { + constraints = [ + "node.platform.os == linux" + ] + platforms { + architecture = "amd64" + os = "linux" + } + } + resources { + limits { + nano_cpus = 30 * 10000000 + memory_bytes = 256 * 1000000 + } + reservation { + nano_cpus = 5 * 10000000 + memory_bytes = 64 * 1000000 + } + } + } + update_config { + 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_agent.name, "/:.*/", "") + } +} diff --git a/docker-portainer-edge-agent/input.tf b/docker-portainer-edge-agent/input.tf new file mode 100644 index 0000000..962f78d --- /dev/null +++ b/docker-portainer-edge-agent/input.tf @@ -0,0 +1,26 @@ +variable "docker" { + type = object({ + name_agent = string + stack_name = optional(string) + }) +} +variable "portainer" { + type = object({ + version = string + }) +} +variable "edge_id" { + type = string + description = "The ID of the edge agent" + default = null +} +variable "edge_key" { + type = string + description = "The key of the edge agent" + default = null +} +variable "debug" { + type = bool + description = "Enable debug mode" + default = false +} diff --git a/docker-portainer-edge-agent/terraform.tf b/docker-portainer-edge-agent/terraform.tf new file mode 100644 index 0000000..6301bc5 --- /dev/null +++ b/docker-portainer-edge-agent/terraform.tf @@ -0,0 +1,18 @@ +terraform { + required_version = "~> 1.6" + + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~> 3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + scratch = { + source = "BrendanThompson/scratch" + version = "~> 0.4" + } + } +} diff --git a/docker-portainer-ui/inputs.tf b/docker-portainer-ui/inputs.tf new file mode 100644 index 0000000..2e5607c --- /dev/null +++ b/docker-portainer-ui/inputs.tf @@ -0,0 +1,16 @@ +variable "docker" { + type = object({ + name = string + stack_name = optional(string) + networks = list(object({ + name = string + id = string + })) + }) +} +variable "portainer" { + type = object({ + version = string + logo = optional(string) + }) +} diff --git a/docker-portainer-ui/output.tf b/docker-portainer-ui/output.tf new file mode 100644 index 0000000..7853309 --- /dev/null +++ b/docker-portainer-ui/output.tf @@ -0,0 +1,9 @@ +output "portainer" { + value = { + credentials = { + username = "admin" # Sorry, this is hardcoded in the portainer image + password = nonsensitive(random_password.password.result) + } + service_name = docker_service.portainer.name + } +} diff --git a/docker-portainer-ui/terraform.tf b/docker-portainer-ui/terraform.tf new file mode 100644 index 0000000..3a46964 --- /dev/null +++ b/docker-portainer-ui/terraform.tf @@ -0,0 +1,18 @@ +terraform { + required_version = "~> 1.6" + + required_providers { + docker = { + source = "kreuzwerker/docker" + version = "~> 3.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + htpasswd = { + source = "loafoe/htpasswd" + version = "~> 1.0" + } + } +} diff --git a/docker-portainer-ui/ui.tf b/docker-portainer-ui/ui.tf new file mode 100644 index 0000000..b4e2fd5 --- /dev/null +++ b/docker-portainer-ui/ui.tf @@ -0,0 +1,106 @@ +resource "random_password" "password" { + length = 32 +} +resource "random_password" "salt" { + length = 8 +} +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}" +} +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 + ] + } +}