variable "hetzner_dns_access_token" {
  type = string
}

job "nginx" {
  datacenters = ["dc1"]
  namespace = "default"

  spread {
    attribute =  "${node.unique.name}"
    weight    = 100
  }

  update {
    stagger      = "30s"
    max_parallel = 1
  }

  group "work" {
    # My cluster node names are cluster-0, cluster-1, etc.
    # I run this job on a single node at a time, and "pin" it to the same node using the below constraint
    # This way, once certificates are generated for my domains, they'll be reused until they expire since
    # the job always runs on the same host where they were generated.
    count = 1
    constraint {
      attribute = "${node.unique.name}"
      operator = "set_contains_any"

      # CHANGE THIS TO THE NAME OF THE NODE THAT WILL RUN NGINX
      value    = "cluster-0"
    }

    # Claim our host volumes, so they can be mounted inside our certbot/nginx jobs
    volume "certbot" {
      type      = "host"
      read_only = false
      source    = "certbot"
    }

    volume "letsencrypt" {
      type      = "host"
      read_only = false
      source    = "letsencrypt"
    }

    network {
      port "http" {
        static = 80
      }
      port "https" {
        static = 443
      }
    }

   # This is the task that fetches certificates for all of our domains and places them on our host volume
   # Doing so makes the certificates available in our `nginx` job
   # Note: This task's lifecycle is "prestart", meaning it must complete before the `nginx` task starts
   task "certbot-all-domains" {
      driver = "docker"
      user = "root"

      config {
        image = "inetsoftware/certbot-dns-hetzner"
        # We use a custom entrypoint so we can script this tasks's behavior
        entrypoint = ["${NOMAD_TASK_DIR}/run.sh"]
      }

      template {
        data = <<EOF
#!/bin/sh

# Note, we're not dealing with bash here. We're dealing with 'ash', as this docker image is BusyBox linux
# Hence, we can't use normal bash arrays. Separate each domain by SPACES, NOTE COMMAS
DOMAINS="example.com foo.com bar.com"

# Set this to an empty string "" for production mode
STAGING="--staging"

DNS_PROPAGATION_SECONDS=30

set -- $DOMAINS

while [ -n "$1" ]; do
  domain=$1
  echo "Domain: $domain"

  certbot certonly -v $STAGING --cert-name ${domain} -d ${domain} -d *.${domain} --agree-tos -m youremail@example.com --keep-until-expiring --authenticator dns-hetzner --dns-hetzner-credentials /alloc/etc/letsencrypt/hetzner-certbot-config.ini --dns-hetzner-propagation-seconds=${DNS_PROPAGATION_SECONDS}

  echo "Done with: $domain"
  shift
done
EOF

        destination   = "${NOMAD_TASK_DIR}/run.sh"
        perms = "755"
      }

      # Make our hetzner DNS token available to the certbot job
      template {
        data = <<EOF
dns_hetzner_api_token = ${var.hetzner_dns_access_token}
EOF

        destination   = "${NOMAD_ALLOC_DIR}/etc/letsencrypt/hetzner-certbot-config.ini"
        change_mode   = "signal"
        change_signal = "SIGHUP"
      }

      resources {
        cpu = 50
        memory = 100
      }


      volume_mount {
        volume      = "certbot"
        destination = "/etc/letsencrypt" #<-- in the container
        read_only   = false
      }

      volume_mount {
        volume      = "letsencrypt"
        destination = "/var/www/certbot" #<-- in the container
        read_only   = false
      }

      lifecycle {
        hook    = "prestart"
        sidecar = false
      }
    }

    task "nginx" {
      driver = "docker"

      template {
        data = <<EOF
# You'll need to manually create `server` blocks for every domain that you host
# Here we provide example server blocks for a single domain
server {
    listen 80;
    listen [::]:80;

    server_tokens off;
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
    gzip_disable "MSIE [1-6]\.";

    server_name example www.example.com;

    # Force TLS
    location / {
        return 301 https://example.com$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_tokens off;
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
    gzip_disable "MSIE [1-6]\.";
    server_name example.com;

    ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;

    location / {

      # I run my application serverrs as well and use nomad service discovery to find them
      # This is provided only for example purpose. If you don't have a nomad job named "example.com-application"
      # this will not work
      {{ range nomadService "example.com-application" }}
      proxy_pass http://{{ .Address }}:{{ .Port }};
      {{ end }}
    }
}
EOF

        destination   = "local/conf/load-balancer.conf"
        change_mode   = "signal"
        change_signal = "SIGHUP"
      }

      config {
        image = "nginx"
        ports = ["http", "https"]
        volumes = [
          # Make our load-balancer.conf available in nginx's config directory
          "local/conf:/etc/nginx/conf.d",
        ]
        privileged = true
      }

      resources {
        cpu = 250
        memory = 250
      }

      # We're mounting the same host volume claims that the certbot task mounted
      volume_mount {
        volume      = "certbot"
        destination = "/etc/nginx/ssl/" #<-- in the container
        read_only   = false
      }

      volume_mount {
        volume      = "letsencrypt"
        destination = "/var/www/certbot" #<-- in the container
        read_only   = false
      }


      service {
        port = "http"

        # I use nomad for service discovery, but many nomad users prefer "consul" for this
        provider = "nomad"

        meta {
          meta = "General purpose load balancer"
        }

        # Take this check with a grain of salt ant tune yours for yourself
        check {
          type     = "tcp"
          port     = "http"
          interval = "10s"
          timeout  = "2s"
        }
      }
    }
  }
}

