2

The error

I try to launch a logspout container and set the log format (an ENV variable) via a docker-compose file. Not too difficult, and if I launch it with docker-compose up, everything works fine. But when I try to launch it with docker swarm init and docker stack deploy -c docker-compose.yml mystack, I get an error:

Error response from daemon: rpc error: code = InvalidArgument desc = expanding env failed: expanding env "RAW_FORMAT={ \"container\" : \"{{ .Container.Name }}\", \"labels\": {{ toJSON .Container.Config.Labels }}, \"timestamp\": \"{{ .Time.Format \"2006-01-02T15:04:05Z07:00\" }}\", \"source\" : \"{{ .Source }}\", \"message\": {{ toJSON .Data }} }": template: expansion:1: function "toJSON" not defined

What I understand

I think I have the error only with swarm and not docker-compose, because the ENV variable I want to pass to logspout is:

RAW_FORMAT: '{ "container" : "{{ .Container.Name }}", "labels": {{ toJSON .Container.Config.Labels }}, "timestamp": "{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{ .Source }}", "message": {{ toJSON .Data }} }'

This ENV variable contains a go template. But with swarm mode, you can create services using go-templates. So it seems that swarm tries (and fails) to parse the value of the ENV variable that I just want to pass to the logspout container.

My question

  1. Is it a way to tell swarm not to parse the go-template in my RAW_FORMAT variable?

  2. If not, is there an other way to set this variable to the correct value?

More...

If you want to reproduce this problem, here is a minimal docker-compose file:

version: "3.3"
services:
logspout:
  image: gliderlabs/logspout:latest
  volumes:
    - /etc/hostname:/etc/host_hostname:ro
    - /var/run/docker.sock:/var/run/docker.sock
  environment:
    RAW_FORMAT: '{ "container" : "{{ .Container.Name }}", "labels": {{ toJSON .Container.Config.Labels }}, "timestamp": "{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{ .Source }}", "message": {{ toJSON .Data }} }'

If you are on windows, you have to execute $Env:COMPOSE_CONVERT_WINDOWS_PATHS=1 first.

OLIVIER
  • 858
  • 9
  • 18

2 Answers2

3

In go templates, escaping {{ can be done with {{"{{"}}, so your yml file should look like this:

version: "3.3"
services:
  logspout:
    image: gliderlabs/logspout:latest
    volumes:
      - /etc/hostname:/etc/host_hostname:ro
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      RAW_FORMAT: '{ "container" : "{{"{{"}} .Container.Name }}", "labels": {{"{{"}} toJSON .Container.Config.Labels }}, "timestamp": "{{"{{"}} .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{"{{"}} .Source }}", "message": {{"{{"}} toJSON .Data }} }'
Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • Hi Constantin. Thanks for your answer. With your solution I do not get the error in swarm, but sadly, logspout seems to escape "{{" too... So now, logs are not working ... – OLIVIER Oct 17 '18 at 15:17
  • @OLIVIER how does the RAW_FORMAT looks like in the container? – Constantin Galbenu Oct 17 '18 at 16:19
  • 1
    @ CONSTANTIN after further testing, your solution seems to work. My bad. The logs were not working because in swarm mode, the .Container.Config.Labels contains .labels.com.docker.swarm.task wich, for some reason, causes a mapping error in elasticsearch. But this is a problem for another day (and maybe another post). Thanks a lot for your help. – OLIVIER Oct 17 '18 at 19:25
3

You can warp by {{` string `}}

  environment:
    RAW_FORMAT: '{{`{ "container" : "{{ .Container.Name }}`}}", "labels": {{ toJSON .Container.Config.Labels }}, "timestamp": "{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}", "source" : "{{ .Source }}", "message": {{ toJSON .Data }} }`}}'

Another example

LDAP_SEARCH_FILTER={{`(sAMAccountName={{username}})`}}
Roman Rhrn Nesterov
  • 3,538
  • 1
  • 28
  • 16