lloesche / valheim-server-docker

Valheim dedicated gameserver with automatic update, World backup, BepInEx and ValheimPlus mod support
https://hub.docker.com/r/lloesche/valheim-server
Apache License 2.0
1.94k stars 272 forks source link

Discord Message & Hook Environment Variables not enumerated properly with docker stack deploy #227

Closed jcolley closed 3 years ago

jcolley commented 3 years ago

First off, thanks for creating this image and providing the awesome documentation for it. The example docker-compose.yml shows environment variables used for both Discord Messages and Discord Webhooks, however when starting the service, it seems the hooks are enumerated before the Messages.

Things to note about my experience with this image / environment:

Here is my docker-compose.yml:

version: "3.8"
services:
  valheim:
    image: lloesche/valheim-server:latest
    cap_add:
      - sys_nice
    volumes:
      - /mnt/storage1.noc2/docker/dev/vanirsdisease.boxspawn.com/data:/opt/valheim
      - /mnt/storage1.noc2/docker/dev/vanirsdisease.boxspawn.com/config:/config
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
    deploy:
      mode: replicated
      replicas: 1
    environment:
      SERVER_NAME: "SomeServerName"
      SERVER_PORT: "2456"
      WORLD_NAME: "Dedicated"
      SERVER_PASS: "nothtepassword"
      SERVER_PUBLIC: "true"
      STATUS_HTTP: "true"
      SUPERVISOR_HTTP: "true"
      SUPERVISOR_HTTP_USER: "admin"
      SUPERVISOR_HTTP_PASS: "notthepassword"

      DISCORD_POST_START: "__Valheim Server is now online.__\nJoin @ valheim.spiffydomain.com \nPassword: **nothtepassword**"
      DISCORD_PRE_RESTART: "**Restarting** Valheim Server in one minute!"
      DISCORD_POST_RESTART: "Valheim Server restart complete."
      DISCORD_PRE_UPDATE: "Checking for Valheim Server updates, since nobody is on..."
      DISCORD_PRE_SERVER_SHUTDOWN: "Valheim Server has been shutdown; most likely for maintenance."
      DISCORD_WEBHOOK: "https://discord.com/api/webhooks/12345678/2134234234_reallylongapihook"

      POST_START_HOOK: 'curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_POST_START\"}" "$DISCORD_WEBHOOK"'
      PRE_RESTART_HOOK: 'curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_PRE_RESTART\"}" "$DISCORD_WEBHOOK" && sleep 60'
      POST_RESTART_HOOK: 'curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_POST_RESTART\"}" "$DISCORD_WEBHOOK"'
      PRE_UPDATE_CHECK_HOOK: 'curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_PRE_UPDATE\"}" "$DISCORD_WEBHOOK"'
      PRE_SERVER_SHUTDOWN_HOOK: 'curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_PRE_SERVER_SHUTDOWN\"}" "$DISCORD_WEBHOOK"'
    networks:
      - my-public-internet-facing-ingress-network

networks:
  my-public-internet-facing-ingress-network:
    external: true

Server logs show the webhook output as follows:

2021-03-14 17:30:06 [44] (INFO) Running pre update check hook: curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"\"}" ""
curl: (3) URL using bad/illegal format or missing URL

Obviously, it's missing the $DISCORD* environment variable values. When I exec into the running server container and echo the values of the environment variables I see that the $DISCORD* environment variables appear to be set correct, but their values are just omitted from the hook variables:

root@edd8d37c5f88:/# echo $DISCORD_POST_RESTART
Valheim Server restart complete.
root@edd8d37c5f88:/# echo $POST_RESTART_HOOK
curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"\"}" ""

While I could always rebuild and embed the environment variables into the image so that they absolutely exist prior to a container instance being created, having to rebuild the image every time I want to add/remove/update messages seems a bit overkill for something this simple - especially since I'm going to need the ability to update these messages from a web-interface at some point.

I feel like I'm overlooking something really simple and just need another pair of eyes to point it out. I hope it's that simple :) See anything obvious?

lloesche commented 3 years ago
jcolley commented 3 years ago

Thanks for the quick response, however this is not a duplicate of #144 since I'm using docker stack deploy whereby variable substitution in env_file's aren't an option.

Note when using docker stack deploy

The .env file feature only works when you use the docker-compose up command and does not work with docker stack deploy.

Source: https://docs.docker.com/compose/compose-file/compose-file-v3/#variable-substitution

I haven't run into any issues without stop_grace_period, but I definitely see the utility of it and will be using it in my compose files from now on - thanks for the tip!

I appreciate your directive of not mounting localtime; I've used both methods in the past with some success. However, I've found that not all containers support the TZ env var out-of-the-box (FROM scratch, as an example...), which is why I just default to mounting localtime; do you have any references showing best practices are different or some other reason that you personally don't mount localtime?

Can we reopen this? I'm happy to work toward a solution with you.

lloesche commented 3 years ago

The issue in #144 is a quoting problem, you are having a quoting problem. The solution is described in there. The solution to your variable substitution is literally described in the docker document you pasted. You don't want any form of substitution so you turn every $ into $$. To solve your quoting problem you encase the entire key=value pair in single quotes.

    environment:
      - DISCORD_WEBHOOK=https://discord.com/api/webhooks/8171522530.......
      - DISCORD_MESSAGE=Starting Valheim server $$SERVER_NAME
      - 'PRE_BOOTSTRAP_HOOK=curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$$(eval echo $$DISCORD_MESSAGE)\"}" "$$DISCORD_WEBHOOK"'

Confirm inside the container that your hook has the correct non-substituted value:

root@143bc0b2b393:/# env | grep _HOOK
PRE_BOOTSTRAP_HOOK=curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$(eval echo $DISCORD_MESSAGE)\"}" "$DISCORD_WEBHOOK"

Support for TZ is not a Docker thing, it's something the container has to explicitly support. See https://github.com/lloesche/valheim-server-docker#environment-variables

By read only mounting the file you make it so the bootstrap script will run into an error when trying to set the timezone in https://github.com/lloesche/valheim-server-docker/blob/c17b17c2b3eb8862c21558df2e7230ca0804793e/bootstrap#L24-L25