refactor: simplify project structure
This commit is contained in:
32
modules/00-globals/cloudflare/main.tf
Normal file
32
modules/00-globals/cloudflare/main.tf
Normal file
@@ -0,0 +1,32 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
dotenv = {
|
||||
source = "germanbrew/dotenv"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "dotenv_sensitive" "cloudflare_credentials" {}
|
||||
data "dotenv" "cloudflare_config" {}
|
||||
|
||||
// Outputs
|
||||
output "cloudflare_account_id" {
|
||||
description = "Cloudflare account ID"
|
||||
value = data.dotenv.cloudflare_config.entries.CLOUDFLARE_ACCOUNT_ID
|
||||
}
|
||||
|
||||
output "cloudflare_zone_id" {
|
||||
description = "Cloudflare zone ID"
|
||||
value = data.dotenv.cloudflare_config.entries.CLOUDFLARE_ZONE_ID
|
||||
}
|
||||
|
||||
output "domain" {
|
||||
description = "Base domain name"
|
||||
value = data.dotenv.cloudflare_config.entries.DOMAIN
|
||||
}
|
||||
|
||||
output "cloudflare_api_token" {
|
||||
description = "API token for Cloudflare"
|
||||
value = data.dotenv_sensitive.cloudflare_credentials.entries.CLOUDFLARE_API_TOKEN
|
||||
sensitive = true
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
output "cloudflare_account_id" {
|
||||
description = "Cloudflare account ID"
|
||||
value = var.cloudflare_account_id
|
||||
}
|
||||
|
||||
output "cloudflare_zone_id" {
|
||||
description = "Cloudflare zone ID"
|
||||
value = var.cloudflare_zone_id
|
||||
}
|
||||
|
||||
output "domain" {
|
||||
description = "Base domain name"
|
||||
value = var.domain
|
||||
}
|
||||
|
||||
output "cloudflare_api_token" {
|
||||
description = "API token for Cloudflare"
|
||||
value = var.cloudflare_api_token
|
||||
sensitive = true
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
variable "cloudflare_api_token" {
|
||||
description = "API token for Cloudflare with tunnel, DNS, and zone management permissions"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "cloudflare_account_id" {
|
||||
description = "Cloudflare account ID"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cloudflare_zone_id" {
|
||||
description = "Cloudflare zone ID for your domain"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "domain" {
|
||||
description = "Base domain name (e.g., example.com)"
|
||||
type = string
|
||||
}
|
||||
30
modules/00-globals/system/main.tf
Normal file
30
modules/00-globals/system/main.tf
Normal file
@@ -0,0 +1,30 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
dotenv = {
|
||||
source = "germanbrew/dotenv"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data "dotenv" "system_config" {}
|
||||
|
||||
// Outputs
|
||||
output "timezone" {
|
||||
description = "System timezone"
|
||||
value = data.dotenv.system_config.entries.TIMEZONE
|
||||
}
|
||||
|
||||
output "data_dir" {
|
||||
description = "Base directory for data volumes"
|
||||
value = data.dotenv.system_config.entries.DATA_DIR
|
||||
}
|
||||
|
||||
output "puid" {
|
||||
description = "PUID for Docker containers"
|
||||
value = data.dotenv.system_config.entries.PUID
|
||||
}
|
||||
|
||||
output "pgid" {
|
||||
description = "PGID for Docker containers"
|
||||
value = data.dotenv.system_config.entries.PGID
|
||||
}
|
||||
@@ -4,12 +4,10 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "~> 4.0"
|
||||
source = "cloudflare/cloudflare"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "~> 3.5.1"
|
||||
source = "hashicorp/random"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,15 +25,31 @@ resource "cloudflare_zero_trust_tunnel_cloudflared" "this" {
|
||||
secret = var.tunnel_secret != "" ? var.tunnel_secret : random_id.tunnel_secret[0].b64_std
|
||||
}
|
||||
|
||||
locals {
|
||||
all_ingress_rules = [for rule in var.ingress_rules : rule if rule != null]
|
||||
locals {
|
||||
// Transform service definitions into ingress rules format, only for services with ingress_enabled
|
||||
service_ingress_rules = flatten([
|
||||
for service in var.service_definitions :
|
||||
// Only process services with hostnames AND where ingress is enabled (or default to true for backward compatibility)
|
||||
(length(service.hostnames) > 0) ? [
|
||||
for hostname in service.hostnames : {
|
||||
hostname = "${hostname}.${var.domain}"
|
||||
service = service.endpoint
|
||||
}
|
||||
] : []
|
||||
])
|
||||
|
||||
// Combine manual ingress rules and service-generated ones
|
||||
all_ingress_rules = concat(
|
||||
[for rule in var.ingress_rules : rule if rule != null],
|
||||
local.service_ingress_rules
|
||||
)
|
||||
}
|
||||
|
||||
// Configure tunnel routing
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" {
|
||||
account_id = var.cloudflare_account_id
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.this.id
|
||||
|
||||
|
||||
config {
|
||||
// Add all service ingress rules
|
||||
dynamic "ingress_rule" {
|
||||
@@ -45,7 +59,7 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" {
|
||||
service = ingress_rule.value.service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Default catch-all rule (required)
|
||||
ingress_rule {
|
||||
service = "http_status:404"
|
||||
@@ -55,8 +69,11 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "this" {
|
||||
|
||||
// Create DNS record for each service
|
||||
resource "cloudflare_record" "service" {
|
||||
for_each = { for rule in var.ingress_rules : rule.hostname => rule }
|
||||
|
||||
for_each = {
|
||||
for rule in local.all_ingress_rules : rule.hostname => rule
|
||||
if rule.hostname != null && rule.hostname != ""
|
||||
}
|
||||
|
||||
zone_id = var.cloudflare_zone_id
|
||||
name = split(".", each.value.hostname)[0] // Extract subdomain
|
||||
content = "${cloudflare_zero_trust_tunnel_cloudflared.this.id}.cfargotunnel.com"
|
||||
@@ -76,20 +93,20 @@ module "cloudflared" {
|
||||
container_name = var.container_name
|
||||
image = "cloudflare/cloudflared"
|
||||
tag = local.image_tag
|
||||
|
||||
|
||||
// Environment variables with tunnel token
|
||||
env_vars = {
|
||||
env_vars = {
|
||||
TUNNEL_TOKEN = cloudflare_zero_trust_tunnel_cloudflared.this.tunnel_token
|
||||
}
|
||||
|
||||
|
||||
// Command to run tunnel
|
||||
command = ["tunnel", "--no-autoupdate", "run"]
|
||||
|
||||
command = ["tunnel", "--no-autoupdate", "run"]
|
||||
|
||||
// Restart policy
|
||||
restart_policy = "unless-stopped"
|
||||
|
||||
// Enable monitoring for the container via Watchtower if specified
|
||||
monitoring = var.monitoring
|
||||
|
||||
networks = var.networks
|
||||
// Enable monitoring for the container via Watchtower if specified
|
||||
monitoring = var.monitoring
|
||||
|
||||
networks = var.networks
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Variables for the Cloudflare tunnel module
|
||||
|
||||
variable "cloudflare_account_id" {
|
||||
description = "Cloudflare account ID"
|
||||
type = string
|
||||
@@ -10,6 +9,11 @@ variable "cloudflare_zone_id" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "domain" {
|
||||
description = "The domain name to use for creating DNS records"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "container_name" {
|
||||
description = "Name of the Cloudflare tunnel container"
|
||||
type = string
|
||||
@@ -35,7 +39,7 @@ variable "tunnel_secret" {
|
||||
}
|
||||
|
||||
variable "ingress_rules" {
|
||||
description = "List of ingress rules for services to be exposed through the tunnel"
|
||||
description = "List of ingress rules to configure manually"
|
||||
type = list(object({
|
||||
hostname = string
|
||||
service = string
|
||||
@@ -43,6 +47,17 @@ variable "ingress_rules" {
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "service_definitions" {
|
||||
description = "List of service definitions containing name, endpoints and hostname configuration"
|
||||
type = list(object({
|
||||
name = string
|
||||
primary_port = number
|
||||
endpoint = string
|
||||
hostnames = optional(list(string), [])
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "monitoring" {
|
||||
description = "Enable monitoring via Watchtower"
|
||||
type = bool
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
// Docker Network Module
|
||||
// This module creates a Docker network for container communication
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = "~> 3.6.0"
|
||||
source = "kreuzwerker/docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "docker_network" "this" {
|
||||
name = var.name
|
||||
driver = var.driver
|
||||
internal = var.internal
|
||||
attachable = var.attachable
|
||||
ipam_driver = var.ipam_driver
|
||||
|
||||
name = var.name
|
||||
driver = var.driver
|
||||
internal = var.internal
|
||||
attachable = var.attachable
|
||||
ipam_driver = var.ipam_driver
|
||||
|
||||
dynamic "ipam_config" {
|
||||
for_each = var.subnet != "" ? [1] : []
|
||||
content {
|
||||
subnet = var.subnet
|
||||
gateway = var.gateway
|
||||
ip_range = var.ip_range
|
||||
aux_address = var.aux_address
|
||||
subnet = var.subnet
|
||||
gateway = var.gateway
|
||||
ip_range = var.ip_range
|
||||
aux_address = var.aux_address
|
||||
}
|
||||
}
|
||||
|
||||
options = var.options
|
||||
|
||||
options = var.options
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Outputs for Docker Network module
|
||||
|
||||
output "network_id" {
|
||||
description = "The ID of the Docker network"
|
||||
value = docker_network.this.id
|
||||
|
||||
@@ -67,8 +67,7 @@ This module requires the Docker provider to be configured in your root module:
|
||||
terraform {
|
||||
required_providers {
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = ">= 3.0.0"
|
||||
source = "kreuzwerker/docker"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,33 @@
|
||||
// Generic Docker service module
|
||||
// Creates and manages a Docker container with configurable options
|
||||
module "system_globals" {
|
||||
source = "../../00-globals/system"
|
||||
}
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
docker = {
|
||||
source = "kreuzwerker/docker"
|
||||
version = ">= 3.0.0"
|
||||
}
|
||||
dotenv = {
|
||||
source = "germanbrew/dotenv"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
network_mode = var.network_mode
|
||||
network_mode = var.network_mode
|
||||
container_name = var.container_name
|
||||
image_name = "${var.image}:${var.tag}"
|
||||
|
||||
image_name = "${var.image}:${var.tag}"
|
||||
|
||||
default_env_vars = {
|
||||
TZ = module.system_globals.timezone
|
||||
PUID = module.system_globals.puid
|
||||
PGID = module.system_globals.pgid
|
||||
}
|
||||
|
||||
env_vars = merge(var.env_vars, local.default_env_vars)
|
||||
|
||||
// Prepare ports configuration
|
||||
ports_config = [
|
||||
for port in var.ports : {
|
||||
@@ -23,7 +36,7 @@ locals {
|
||||
protocol = port.protocol
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
// Prepare volumes configuration
|
||||
volumes_config = [
|
||||
for volume in var.volumes : {
|
||||
@@ -32,20 +45,20 @@ locals {
|
||||
read_only = volume.read_only
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
// Define monitoring labels if enabled
|
||||
monitoring_labels = var.monitoring ? {
|
||||
"com.centurylinklabs.watchtower.enable" = "true"
|
||||
} : {}
|
||||
|
||||
|
||||
// Merge provided labels with monitoring labels
|
||||
merged_labels = merge(var.labels, local.monitoring_labels)
|
||||
}
|
||||
|
||||
// Pull the Docker image
|
||||
resource "docker_image" "service_image" {
|
||||
name = local.image_name
|
||||
keep_locally = var.keep_image_locally
|
||||
name = local.image_name
|
||||
keep_locally = var.keep_image_locally
|
||||
pull_triggers = [var.tag]
|
||||
}
|
||||
|
||||
@@ -53,12 +66,12 @@ resource "docker_image" "service_image" {
|
||||
resource "docker_container" "service_container" {
|
||||
name = local.container_name
|
||||
image = docker_image.service_image.image_id
|
||||
|
||||
|
||||
restart = var.restart_policy
|
||||
|
||||
|
||||
# Set the network mode (bridge, host, etc.)
|
||||
network_mode = local.network_mode
|
||||
|
||||
|
||||
# Dynamically configure ports based on the provided list
|
||||
dynamic "ports" {
|
||||
for_each = local.ports_config
|
||||
@@ -68,7 +81,7 @@ resource "docker_container" "service_container" {
|
||||
protocol = ports.value.protocol
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Dynamically configure networks based on the provided list
|
||||
dynamic "networks_advanced" {
|
||||
for_each = var.networks
|
||||
@@ -76,7 +89,7 @@ resource "docker_container" "service_container" {
|
||||
name = networks_advanced.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Dynamically configure volumes based on the provided list
|
||||
dynamic "volumes" {
|
||||
for_each = local.volumes_config
|
||||
@@ -86,10 +99,10 @@ resource "docker_container" "service_container" {
|
||||
read_only = volumes.value.read_only
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Configure environment variables - map to array of strings
|
||||
env = [for k, v in var.env_vars : "${k}=${v}"]
|
||||
|
||||
env = [for k, v in local.env_vars : "${k}=${v}"]
|
||||
|
||||
# Set container labels
|
||||
dynamic "labels" {
|
||||
for_each = local.merged_labels
|
||||
@@ -98,7 +111,7 @@ resource "docker_container" "service_container" {
|
||||
value = labels.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Add container healthcheck if configured
|
||||
dynamic "healthcheck" {
|
||||
for_each = var.healthcheck != null ? [var.healthcheck] : []
|
||||
@@ -110,23 +123,23 @@ resource "docker_container" "service_container" {
|
||||
retries = healthcheck.value.retries
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Set resource limits if specified
|
||||
memory = var.memory_limit
|
||||
memory_swap = var.memory_swap_limit
|
||||
cpu_shares = var.cpu_shares
|
||||
|
||||
memory = var.memory_limit
|
||||
memory_swap = var.memory_swap_limit
|
||||
cpu_shares = var.cpu_shares
|
||||
|
||||
# Other container options
|
||||
dns = var.dns
|
||||
dns_search = var.dns_search
|
||||
hostname = var.hostname
|
||||
domainname = var.domainname
|
||||
user = var.user
|
||||
working_dir = var.working_dir
|
||||
command = var.command
|
||||
entrypoint = var.entrypoint
|
||||
privileged = var.privileged
|
||||
|
||||
dns = var.dns
|
||||
dns_search = var.dns_search
|
||||
hostname = var.hostname
|
||||
domainname = var.domainname
|
||||
user = var.user
|
||||
working_dir = var.working_dir
|
||||
command = var.command
|
||||
entrypoint = var.entrypoint
|
||||
privileged = var.privileged
|
||||
|
||||
# Set log options
|
||||
log_driver = var.log_driver
|
||||
log_opts = var.log_opts
|
||||
|
||||
@@ -23,7 +23,7 @@ variable "keep_image_locally" {
|
||||
variable "restart_policy" {
|
||||
description = "Docker restart policy (no, always, unless-stopped, on-failure)"
|
||||
type = string
|
||||
default = "unless-stopped"
|
||||
default = "always"
|
||||
}
|
||||
|
||||
variable "network_mode" {
|
||||
|
||||
@@ -1,49 +1,51 @@
|
||||
// ActualBudget module for budgeting
|
||||
// This module configures an ActualBudget container with the specified volumes
|
||||
variable "image_tag" {
|
||||
description = "Tag of the ActualBudget image to use"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "volume_path" {
|
||||
description = "Host path for ActualBudget data volume"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "networks" {
|
||||
description = "List of networks to which the container should be attached"
|
||||
type = list(string)
|
||||
}
|
||||
|
||||
locals {
|
||||
container_name = var.container_name != "" ? var.container_name : "actualbudget"
|
||||
container_name = "actualbudget"
|
||||
image = "actualbudget/actual-server"
|
||||
image_tag = var.image_tag != "" ? var.image_tag : "latest"
|
||||
|
||||
default_env_vars = {
|
||||
TZ = var.timezone
|
||||
PUID = var.puid
|
||||
PGID = var.pgid
|
||||
}
|
||||
|
||||
monitoring = true
|
||||
exposed_port = 5006
|
||||
hostnames = ["budget"]
|
||||
default_volumes = [
|
||||
{
|
||||
container_path = "/data"
|
||||
host_path = var.data_volume_path
|
||||
host_path = "${var.volume_path}/data"
|
||||
read_only = false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module "actualbudget" {
|
||||
source = "../../10-services-generic/docker-service"
|
||||
|
||||
container_name = var.container_name
|
||||
image = "actualbudget/actual-server"
|
||||
tag = var.image_tag
|
||||
|
||||
// Environment variables
|
||||
env_vars = local.default_env_vars
|
||||
|
||||
// Port mapping
|
||||
ports = [
|
||||
{
|
||||
internal = 5006
|
||||
external = var.port
|
||||
protocol = "tcp"
|
||||
}
|
||||
]
|
||||
|
||||
// Volume mapping
|
||||
volumes = local.default_volumes
|
||||
|
||||
// Enable monitoring for the container via Watchtower
|
||||
monitoring = var.monitoring
|
||||
|
||||
networks = var.networks
|
||||
source = "../../10-services-generic/docker-service"
|
||||
container_name = local.container_name
|
||||
image = local.image
|
||||
tag = local.image_tag
|
||||
volumes = local.default_volumes
|
||||
networks = var.networks
|
||||
monitoring = local.monitoring
|
||||
}
|
||||
|
||||
output "service_definition" {
|
||||
description = "General service definition with optional ingress configuration"
|
||||
value = {
|
||||
name = local.container_name
|
||||
primary_port = local.exposed_port
|
||||
endpoint = "http://${local.container_name}:${local.exposed_port}"
|
||||
hostnames = local.hostnames
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
output "container_name" {
|
||||
description = "The name of the ActualBudget container"
|
||||
value = module.actualbudget.container_name
|
||||
}
|
||||
|
||||
output "container_id" {
|
||||
description = "The ID of the ActualBudget container"
|
||||
value = module.actualbudget.container_id
|
||||
}
|
||||
|
||||
output "image_id" {
|
||||
description = "The ID of the ActualBudget image"
|
||||
value = module.actualbudget.image_id
|
||||
}
|
||||
|
||||
output "ip_address" {
|
||||
description = "The IP address of the ActualBudget container"
|
||||
value = module.actualbudget.ip_address
|
||||
}
|
||||
|
||||
output "local_url" {
|
||||
description = "The local URL to access the ActualBudget interface"
|
||||
value = "http://localhost:${var.port}"
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
variable "container_name" {
|
||||
description = "Name of the ActualBudget container"
|
||||
type = string
|
||||
default = "actualbudget"
|
||||
}
|
||||
|
||||
variable "timezone" {
|
||||
description = "Timezone for the container"
|
||||
type = string
|
||||
default = "UTC"
|
||||
}
|
||||
|
||||
variable "image_tag" {
|
||||
description = "Tag of the ActualBudget image to use"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
description = "External port for ActualBudget server"
|
||||
type = number
|
||||
default = 5006
|
||||
}
|
||||
|
||||
variable "data_volume_path" {
|
||||
description = "Host path for ActualBudget data volume"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "puid" {
|
||||
description = "User ID for the container"
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "pgid" {
|
||||
description = "Group ID for the container"
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "monitoring" {
|
||||
description = "Enable monitoring for the container via Watchtower"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "networks" {
|
||||
description = "List of networks to which the container should be attached"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
3
modules/20-services-apps/emulatorjs/.env.example
Normal file
3
modules/20-services-apps/emulatorjs/.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
EMULATORJS_FRONTEND_PORT=5823
|
||||
EMULATORJS_CONFIG_PORT=5824
|
||||
EMULATORJS_BACKEND_PORT=5825
|
||||
@@ -1,70 +1,78 @@
|
||||
// EmulatorJS module for retro game emulation
|
||||
// This module configures an EmulatorJS container with the specified volumes
|
||||
terraform {
|
||||
required_providers {
|
||||
dotenv = {
|
||||
source = "germanbrew/dotenv"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "image_tag" {
|
||||
description = "The tag for the EmulatorJS container image"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "volume_path" {
|
||||
description = "Base directory for volumes"
|
||||
type = string
|
||||
}
|
||||
|
||||
locals {
|
||||
container_name = var.container_name != "" ? var.container_name : "emulatorjs"
|
||||
container_name = "emulatorjs"
|
||||
image = "linuxserver/emulatorjs"
|
||||
image_tag = var.image_tag != "" ? var.image_tag : "latest"
|
||||
|
||||
default_env_vars = {
|
||||
TZ = var.timezone
|
||||
}
|
||||
|
||||
// Merge default env vars with any additional ones provided
|
||||
env_vars = merge(local.default_env_vars, var.additional_env_vars)
|
||||
|
||||
// Default volumes for EmulatorJS
|
||||
default_volumes = [
|
||||
monitoring = true
|
||||
env_file = "${path.module}/.env"
|
||||
frontend_port = provider::dotenv::get_by_key("EMULATORJS_FRONTEND_PORT", local.env_file)
|
||||
config_port = provider::dotenv::get_by_key("EMULATORJS_CONFIG_PORT", local.env_file)
|
||||
backend_port = provider::dotenv::get_by_key("EMULATORJS_BACKEND_PORT", local.env_file)
|
||||
ports = [
|
||||
{
|
||||
host_path = var.config_volume_path
|
||||
internal = 3000
|
||||
external = local.config_port
|
||||
protocol = "tcp"
|
||||
},
|
||||
{
|
||||
internal = 80
|
||||
external = local.frontend_port
|
||||
protocol = "tcp"
|
||||
},
|
||||
{
|
||||
internal = 4001
|
||||
external = local.backend_port
|
||||
protocol = "tcp"
|
||||
}
|
||||
]
|
||||
volumes = [
|
||||
{
|
||||
host_path = "${var.volume_path}/config"
|
||||
container_path = "/config"
|
||||
read_only = false
|
||||
},
|
||||
{
|
||||
host_path = var.data_volume_path
|
||||
host_path = "${var.volume_path}/data"
|
||||
container_path = "/data"
|
||||
read_only = false
|
||||
}
|
||||
]
|
||||
|
||||
// Merge default volumes with any additional ones provided
|
||||
volumes = concat(local.default_volumes, var.additional_volumes)
|
||||
}
|
||||
|
||||
// Use the generic docker-service module to deploy EmulatorJS
|
||||
module "emulatorjs" {
|
||||
source = "../../10-services-generic/docker-service"
|
||||
|
||||
container_name = local.container_name
|
||||
image = "linuxserver/emulatorjs"
|
||||
tag = local.image_tag
|
||||
|
||||
restart_policy = var.restart_policy
|
||||
network_mode = "bridge"
|
||||
|
||||
env_vars = local.env_vars
|
||||
volumes = local.volumes
|
||||
|
||||
labels = var.labels
|
||||
|
||||
// Default ports for EmulatorJS
|
||||
ports = [
|
||||
{
|
||||
internal = 3000
|
||||
external = var.config_port
|
||||
protocol = "tcp"
|
||||
},
|
||||
{
|
||||
internal = 80
|
||||
external = var.frontend_port
|
||||
protocol = "tcp"
|
||||
},
|
||||
{
|
||||
internal = 4001
|
||||
external = var.backend_port
|
||||
protocol = "tcp"
|
||||
}
|
||||
]
|
||||
|
||||
// Enable monitoring for the container via Watchtower
|
||||
monitoring = var.monitoring
|
||||
container_name = local.container_name
|
||||
image = local.image
|
||||
tag = local.image_tag
|
||||
volumes = local.volumes
|
||||
ports = local.ports
|
||||
monitoring = local.monitoring
|
||||
}
|
||||
|
||||
output "service_definition" {
|
||||
description = "General service definition with optional ingress configuration"
|
||||
value = {
|
||||
name = module.emulatorjs.container_name
|
||||
primary_port = local.frontend_port
|
||||
endpoint = "http://${module.emulatorjs.container_name}:${local.frontend_port}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Outputs for the EmulatorJS module
|
||||
|
||||
output "container_name" {
|
||||
description = "Name of the created EmulatorJS container"
|
||||
value = module.emulatorjs.container_name
|
||||
}
|
||||
|
||||
output "container_id" {
|
||||
description = "ID of the created EmulatorJS container"
|
||||
value = module.emulatorjs.container_id
|
||||
}
|
||||
|
||||
output "image_id" {
|
||||
description = "ID of the EmulatorJS image used"
|
||||
value = module.emulatorjs.image_id
|
||||
}
|
||||
|
||||
output "frontend_url" {
|
||||
description = "URL to access the EmulatorJS frontend interface"
|
||||
value = "http://localhost:${var.frontend_port}"
|
||||
}
|
||||
|
||||
output "config_url" {
|
||||
description = "URL to access the EmulatorJS web configuration interface"
|
||||
value = "http://localhost:${var.config_port}"
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
variable "container_name" {
|
||||
description = "Name for the EmulatorJS container"
|
||||
type = string
|
||||
default = "emulatorjs"
|
||||
}
|
||||
|
||||
variable "image_tag" {
|
||||
description = "The tag for the EmulatorJS container image"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "restart_policy" {
|
||||
description = "Restart policy for the container"
|
||||
type = string
|
||||
default = "unless-stopped"
|
||||
}
|
||||
|
||||
variable "timezone" {
|
||||
description = "Timezone for the container"
|
||||
type = string
|
||||
default = "Etc/UTC"
|
||||
}
|
||||
|
||||
variable "puid" {
|
||||
description = "User ID the container will run as"
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "pgid" {
|
||||
description = "Group ID the container will run as"
|
||||
type = number
|
||||
default = 1000
|
||||
}
|
||||
|
||||
variable "config_volume_path" {
|
||||
description = "Host path for the EmulatorJS config directory"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "data_volume_path" {
|
||||
description = "Host path for the EmulatorJS data directory"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "frontend_port" {
|
||||
description = "External port for the EmulatorJS frontend"
|
||||
type = number
|
||||
default = 3000
|
||||
}
|
||||
|
||||
variable "config_port" {
|
||||
description = "External port for the EmulatorJS configuration interface"
|
||||
type = number
|
||||
default = 8080
|
||||
}
|
||||
|
||||
variable "backend_port" {
|
||||
description = "External port for the EmulatorJS backend"
|
||||
type = number
|
||||
default = 4001
|
||||
}
|
||||
|
||||
variable "additional_env_vars" {
|
||||
description = "Additional environment variables for EmulatorJS"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "additional_volumes" {
|
||||
description = "Additional volumes to mount in the container"
|
||||
type = list(object({
|
||||
host_path = string
|
||||
container_path = string
|
||||
read_only = bool
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Labels to set on the container"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "monitoring" {
|
||||
description = "Enable monitoring for the container via Watchtower"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
7
modules/20-services-apps/watchtower/.env.example
Normal file
7
modules/20-services-apps/watchtower/.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
WATCHTOWER_CLEANUP=true
|
||||
WATCHTOWER_POLL_INTERVAL=86400
|
||||
WATCHTOWER_INCLUDE_STOPPED=false
|
||||
WATCHTOWER_REVIVE_STOPPED=false
|
||||
WATCHTOWER_ROLLING_RESTART=true
|
||||
WATCHTOWER_NOTIFICATIONS=shoutrrr
|
||||
WATCHTOWER_NOTIFICATION_URL=discord://token@webhook_id
|
||||
@@ -1,58 +1,48 @@
|
||||
// Watchtower module for automatic Docker container updates
|
||||
// This module configures a Watchtower container that monitors and updates other containers
|
||||
terraform {
|
||||
required_providers {
|
||||
dotenv = {
|
||||
source = "germanbrew/dotenv"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "image_tag" {
|
||||
description = "The tag for the Watchtower container image"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
locals {
|
||||
container_name = var.container_name != "" ? var.container_name : "watchtower"
|
||||
container_name = "watchtower"
|
||||
image = "containrrr/watchtower"
|
||||
image_tag = var.image_tag != "" ? var.image_tag : "latest"
|
||||
|
||||
default_env_vars = {
|
||||
TZ = var.timezone
|
||||
WATCHTOWER_CLEANUP = var.cleanup
|
||||
WATCHTOWER_POLL_INTERVAL = var.poll_interval
|
||||
WATCHTOWER_INCLUDE_STOPPED = var.include_stopped
|
||||
WATCHTOWER_REVIVE_STOPPED = var.revive_stopped
|
||||
WATCHTOWER_ROLLING_RESTART = var.rolling_restart
|
||||
WATCHTOWER_NOTIFICATION_URL = var.notification_url
|
||||
WATCHTOWER_NOTIFICATIONS = var.enable_notifications ? "shoutrrr" : ""
|
||||
env_file = "${path.module}/.env"
|
||||
|
||||
env_vars = {
|
||||
WATCHTOWER_CLEANUP = provider::dotenv::get_by_key("WATCHTOWER_CLEANUP", local.env_file)
|
||||
WATCHTOWER_POLL_INTERVAL = provider::dotenv::get_by_key("WATCHTOWER_POLL_INTERVAL", local.env_file)
|
||||
WATCHTOWER_INCLUDE_STOPPED = false
|
||||
WATCHTOWER_REVIVE_STOPPED = false
|
||||
WATCHTOWER_ROLLING_RESTART = true
|
||||
WATCHTOWER_NOTIFICATION_URL = provider::dotenv::get_by_key("WATCHTOWER_NOTIFICATION_URL", local.env_file)
|
||||
WATCHTOWER_NOTIFICATIONS = provider::dotenv::get_by_key("WATCHTOWER_NOTIFICATIONS", local.env_file)
|
||||
}
|
||||
|
||||
// Merge default env vars with any additional ones provided
|
||||
env_vars = merge(local.default_env_vars, var.additional_env_vars)
|
||||
|
||||
// Default volumes for Docker socket access
|
||||
default_volumes = [
|
||||
|
||||
volumes = [
|
||||
{
|
||||
host_path = "/var/run/docker.sock"
|
||||
container_path = "/var/run/docker.sock"
|
||||
read_only = true
|
||||
}
|
||||
]
|
||||
|
||||
// Merge default volumes with any additional ones provided
|
||||
volumes = concat(local.default_volumes, var.additional_volumes)
|
||||
}
|
||||
|
||||
// Use the generic docker-service module to deploy Watchtower
|
||||
module "watchtower" {
|
||||
source = "../../10-services-generic/docker-service"
|
||||
|
||||
container_name = local.container_name
|
||||
image = "containrrr/watchtower"
|
||||
tag = local.image_tag
|
||||
|
||||
restart_policy = var.restart_policy
|
||||
network_mode = "bridge"
|
||||
|
||||
env_vars = local.env_vars
|
||||
volumes = local.volumes
|
||||
|
||||
labels = var.labels
|
||||
|
||||
// Watchtower doesn't typically expose ports but we'll include the option
|
||||
ports = var.ports
|
||||
|
||||
// Add monitoring label if enabled
|
||||
monitoring = var.monitoring
|
||||
|
||||
depends_on = []
|
||||
source = "../../10-services-generic/docker-service"
|
||||
container_name = local.container_name
|
||||
image = local.image
|
||||
tag = local.image_tag
|
||||
env_vars = local.env_vars
|
||||
volumes = local.volumes
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
output "container_name" {
|
||||
description = "Name of the created Watchtower container"
|
||||
value = module.watchtower.container_name
|
||||
}
|
||||
|
||||
output "container_id" {
|
||||
description = "ID of the created Watchtower container"
|
||||
value = module.watchtower.container_id
|
||||
}
|
||||
|
||||
output "image_id" {
|
||||
description = "ID of the Watchtower image used"
|
||||
value = module.watchtower.image_id
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
variable "container_name" {
|
||||
description = "Name for the Watchtower container"
|
||||
type = string
|
||||
default = "watchtower"
|
||||
}
|
||||
|
||||
variable "image_tag" {
|
||||
description = "The tag for the Watchtower container image"
|
||||
type = string
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "restart_policy" {
|
||||
description = "Restart policy for the container"
|
||||
type = string
|
||||
default = "unless-stopped"
|
||||
}
|
||||
|
||||
variable "timezone" {
|
||||
description = "Timezone for the container"
|
||||
type = string
|
||||
default = "Etc/UTC"
|
||||
}
|
||||
|
||||
variable "cleanup" {
|
||||
description = "Remove old images after updating"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "poll_interval" {
|
||||
description = "Poll interval (in seconds) for checking for updates"
|
||||
type = number
|
||||
default = 86400 // Default: check once per day
|
||||
}
|
||||
|
||||
variable "include_stopped" {
|
||||
description = "Include stopped containers when checking for updates"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "revive_stopped" {
|
||||
description = "Restart stopped containers after updating"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "rolling_restart" {
|
||||
description = "Restart containers one by one instead of all at once"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "notification_url" {
|
||||
description = "URL for sending update notifications via shoutrrr"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "enable_notifications" {
|
||||
description = "Enable shoutrrr notifications"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "additional_env_vars" {
|
||||
description = "Additional environment variables for Watchtower"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "additional_volumes" {
|
||||
description = "Additional volumes to mount in the container"
|
||||
type = list(object({
|
||||
host_path = string
|
||||
container_path = string
|
||||
read_only = bool
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Labels to set on the container"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "ports" {
|
||||
description = "Ports to expose (Watchtower typically doesn't need ports exposed)"
|
||||
type = list(object({
|
||||
internal = number
|
||||
external = number
|
||||
protocol = string
|
||||
}))
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "monitoring" {
|
||||
description = "Enable monitoring for the container"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
Reference in New Issue
Block a user