3

I'm using the following template in consul-template:

{{ range services }}
  {{ $server_name := .Name | replaceAll "_" "." }}
  {{ range .Tags }}
    {{ if . | regexMatch "server_name=" }}
      # found matching server_name in {{ . }}
      {{ $server_name := . | regexReplaceAll ".*=" "" }}
    {{ end }}
  {{ end }}
  # server_name = {{ $server_name }}
        acl host_{{ .Name }} hdr(host) -i {{ $server_name }}
        use_backend {{ .Name }}_backend if host_{{ .Name }}
{{ end }}

which produces

  # found matching server_name in server_name=geoserver.hello.org






  # server_name = geoserver.dev.hello.org
        acl host_geoserver_dev_hello_org hdr(host) -i geoserver.dev.hello.org
        use_backend geoserver_dev_hello_org_backend if host_geoserver_dev_hello_org

where .Name is geoserver_dev_hello_org and there is a server_name=geoserver.hello.org tag. I expect that by the end of the .Tags range loop, $server_name should have value geoserver.hello.org, but it still has its original value of geoserver.dev.hello.org.

How can I make it so that the loop overrides $server_name (and exit the loop when the value is found)?

raphink
  • 3,625
  • 1
  • 28
  • 39
  • Possible duplicate of [In a Go template range loop, are variables declared outside the loop reset on each iteration?](http://stackoverflow.com/questions/28674199/in-a-go-template-range-loop-are-variables-declared-outside-the-loop-reset-on-ea) – icza Jan 13 '16 at 10:41

3 Answers3

5

In a consul-template template, you can use the scratch - a temporary key-value store available throughout the lifetime of a template.

Your code would look like this:

{{ range services }}
  {{ $server_name := .Name | replaceAll "_" "." }}
  {{ scratch.Set "server_name" $server_name }}
  {{ range .Tags }}
    {{ if . | regexMatch "server_name=" }}
      # found matching server_name in {{ . }}
      {{ $server_name := . | regexReplaceAll ".*=" "" }}
      {{ scratch.Set "server_name" $server_name }}
    {{ end }}
  {{ end }}
  # server_name = {{ scratch.Get "server_name" }}
        acl host_{{ .Name }} hdr(host) -i {{ $server_name }}
        use_backend {{ .Name }}_backend if host_{{ .Name }}
{{ end }}
vassilvk
  • 196
  • 3
  • 5
2

Inner $server_name and outer $server_name are different variables. You can't set a variable from outer scope in Go templates: http://play.golang.org/p/0fuOmqXrSK.

You could try and rewrite your template to print the acl etc. part inside the inner if which would work unless you need to execute that part just once. Go templates are not designed as a scripting language for complex logic, it's a tool for showing pre-computed information. A fmt.Printf on steroids if you will. All logic including search and replacement should be in Go, it'll be faster, safer, easier to maintain and debug.

Ainar-G
  • 34,563
  • 13
  • 93
  • 119
  • Thank you. In consul-template, I'm guessing I'd need to write a [plugin](https://github.com/hashicorp/consul-template#plugins) to in order to write Go code for that? – raphink Jan 13 '16 at 09:30
  • @ℝaphink Looking at their docs it seems like you can write a plugin in any language, not just Go. Also, now that I think of it, moving the part after the loop inside is not actually a bad idea, unless you need to ensure that it only prints once. – Ainar-G Jan 13 '16 at 10:01
  • Well in that case actually, it wouldn't hurt if I printed a line for `server_name` as well as `.Name`, so I might actually do that. – raphink Jan 13 '16 at 10:03
1

Since Go 1.11 release it's possible to modify variables via assignements within templates. So this is working now:

  {{ $v := "init" }}
  {{ if true }}
    {{ $v = "changed" }}
  {{ end }}
  v: {{ $v }} {{/* "changed" */}}

This version was introduced to Consul Template with the v0.25.1 release.