Giter Site home page Giter Site logo

hashicorp / consul-template Goto Github PK

View Code? Open in Web Editor NEW
4.7K 333.0 784.0 56.71 MB

Template rendering, notifier, and supervisor for @HashiCorp Consul and Vault data.

Home Page: https://www.hashicorp.com/

License: Mozilla Public License 2.0

Go 99.21% Makefile 0.19% Shell 0.17% Dockerfile 0.11% HCL 0.32%
golang consul vault

consul-template's Introduction


Consul Template

build ci Go Documentation

This project provides a convenient way to populate values from Consul into the file system using the consul-template daemon.

The daemon consul-template queries a Consul, Vault, or Nomad cluster and updates any number of specified templates on the file system. As an added bonus, it can optionally run arbitrary commands when the update process completes. Please see the examples folder for some scenarios where this functionality might prove useful.


The documentation in this README corresponds to the main branch of Consul Template. It may contain unreleased features or different APIs than the most recently released version.

Please see the Git tag that corresponds to your version of Consul Template for the proper documentation.


Table of Contents

Community Support

If you have questions about how consul-template works, its capabilities or anything other than a bug or feature request (use github's issue tracker for those), please see our community support resources.

Community portal: https://discuss.hashicorp.com/tags/c/consul/29/consul-template

Other resources: https://www.consul.io/community.html

Additionally, for issues and pull requests, we'll be using the 👍 reactions as a rough voting system to help gauge community priorities. So please add 👍 to any issue or pull request you'd like to see worked on. Thanks.

Installation

  1. Download a pre-compiled, released version from the Consul Template releases page.

  2. Extract the binary using unzip or tar.

  3. Move the binary into $PATH.

To compile from source, please see the instructions in the contributing section.

Quick Example

This short example assumes Consul is installed locally.

  1. Start a Consul cluster in dev mode:
$ consul agent -dev
  1. Author a template in.tpl to query the kv store:
{{ key "foo" }}
  1. Start Consul Template:
$ consul-template -template "in.tpl:out.txt" -once
  1. Write data to the key in Consul:
$ consul kv put foo bar
  1. Observe Consul Template has written the file out.txt:
$ cat out.txt
bar

For more examples and use cases, please see the examples folder in this repository.

Learn Guides

In addition to these examples, HashiCorp has published guides and official documentation to help walk through a few common use cases for Consul Template.

Configuration

Configuration documentation has been moved to docs/configuration.md.

Reload Configuration and Templates

While there are multiple ways to run Consul Template, the most common pattern is to run Consul Template as a system service. When Consul Template first starts, it reads any configuration files and templates from disk and loads them into memory. From that point forward, changes to the files on disk do not propagate to running process without a reload.

The reason for this behavior is simple and aligns with other tools like haproxy. A user may want to perform pre-flight validation checks on the configuration or templates before loading them into the process. Additionally, a user may want to update configuration and templates simultaneously. Having Consul Template automatically watch and reload those files on changes is both operationally dangerous and against some of the paradigms of modern infrastructure. Instead, Consul Template listens for the SIGHUP syscall to trigger a configuration reload. If you update configuration or templates, simply send HUP to the running Consul Template process and Consul Template will reload all the configurations and templates from disk.

Templating Language

Templating Language documentation has been moved to docs/templating-language.md.

Caveats

Docker Image Use

The Alpine Docker image is configured to support an external volume to render shared templates to. If mounted you will need to make sure that the consul-template user in the docker image has write permissions to the directory. Also if you build your own image using these you need to be sure you have the permissions correct.

The consul-template user in docker has a UID of 100 and a GID of 1000.

This effects the in image directories /consul-template/config, used to add configuration when using this as a parent image, and /consul-template/data, exported as a VOLUME as a location to render shared results.

Previously the image initially ran as root in order to ensure the permissions allowed it. But this ran against docker best practices and security policies.

If you build your own image based on ours you can override these values with --build-arg parameters.

Dots in Service Names

Using dots . in service names will conflict with the use of dots for TAG delineation in the template. Dots already interfere with using DNS for service names, so we recommend avoiding dots wherever possible.

Termination on Error

By default Consul Template is highly fault-tolerant. If Consul is unreachable or a template changes, Consul Template will happily continue running. The only exception to this rule is if the optional command exits non-zero. In this case, Consul Template will also exit non-zero. The reason for this decision is so the user can easily configure something like Upstart or God to manage Consul Template as a service.

If you want Consul Template to continue watching for changes, even if the optional command argument fails, you can append || true to your command. Note that || is a "shell-ism", not a built-in function. You will also need to run your command under a shell:

$ consul-template \
  -template "in.ctmpl:out.file:/bin/bash -c 'service nginx restart || true'"

In this example, even if the Nginx restart command returns non-zero, the overall function will still return an OK exit code; Consul Template will continue to run as a service. Additionally, if you have complex logic for restarting your service, you can intelligently choose when you want Consul Template to exit and when you want it to continue to watch for changes. For these types of complex scripts, we recommend using a custom sh or bash script instead of putting the logic directly in the consul-template command or configuration file.

Commands

Environment

The current processes environment is used when executing commands with the following additional environment variables:

  • CONSUL_HTTP_ADDR
  • CONSUL_HTTP_TOKEN
  • CONSUL_HTTP_TOKEN_FILE
  • CONSUL_HTTP_AUTH
  • CONSUL_HTTP_SSL
  • CONSUL_HTTP_SSL_VERIFY
  • NOMAD_ADDR
  • NOMAD_NAMESPACE
  • NOMAD_TOKEN

These environment variables are exported with their current values when the command executes. Other Consul tooling reads these environment variables, providing smooth integration with other Consul tools (like consul maint or consul lock). Additionally, exposing these environment variables gives power users the ability to further customize their command script.

Multiple Commands

The command configured for running on template rendering must take one of two forms.

The first is as a list of the command and arguments split at spaces. The command can use an absolute path or be found on the execution environment's PATH and must be the first item in the list. This form allows for single or multi-word commands that can be executed directly with a system call. For example...

command = ["echo", "hello"] command = ["/opt/foo-package/bin/run-foo"] command = ["foo"]

Note that if you give a single command without the list denoting square brackets ([]) it is converted into a list with a single argument.

This: command = "foo" is equivalent to: command = ["foo"]

The second form is as a single quoted command using system shell features. This form requires a shell named sh be on the executable search path (eg. PATH on *nix). This is the standard on all *nix systems and should work out of the box on those systems. This won't work on, for example, Docker images with only the executable and without a minimal system like Alpine. Using this form you can join multiple commands with logical operators, && and ||, use pipelines with |, conditionals, etc. Note that the shell sh is normally /bin/sh on *nix systems and is either a POSIX shell or a shell run in POSIX compatible mode, so it is best to stick to POSIX shell syntax in this command. For example..

command = "/opt/foo && /opt/bar"

command = "if /opt/foo ; then /opt/bar ; fi"

Using this method you can run as many shell commands as you need with whatever logic you need. Though it is suggested that if it gets too long you might want to wrap it in a shell script, deploy and run that.

Shell Commands and Exec Mode

Using the system shell based command has one additional caveat when used for the Exec mode process (the managed, executed process to which it will propagate signals). That is to get signals to work correctly means not only does anything the shell runs need to handle signals, but the shell itself needs to handle them. This needs to be managed by you as shells will exit upon receiving most signals.

A common example of this would be wanting the SIGHUP signal to trigger a reload of the underlying process and to be ignored by the shell process. To get this you have 2 options, you can use trap to ignore the signal or you can use exec to replace the shell with another process.

To use trap to ignore the signal, you call trap to catch the signal in the shell with no action. For example if you had an underlying nginx process you wanted to run with a shell command and have the shell ignore it you'd do..

command = "trap '' HUP; /usr/sbin/nginx -c /etc/nginx/nginx.conf"

The trap '' HUP; bit is enough to get the shell to ignore the HUP signal. If you left off the trap command nginx would reload but the shell command would exit but leave the nginx still running, not unmanaged.

Alternatively using exec will replace the shell's process with a sub-process, keeping the same PID and process grouping (allowing the sub-process to be managed). This is simpler, but a bit less flexible than trap, and looks like..

command = "exec /usr/sbin/nginx -c /etc/nginx/nginx.conf"

Where the nginx process would replace the enclosing shell process to be managed by consul-template, receiving the Signals directly. Basically exec eliminates the shell from the equation.

See your shell's documentation on trap and/or exec for more details on this.

Multi-phase Execution

Consul Template does an n-pass evaluation of templates, accumulating dependencies on each pass. This is required due to nested dependencies, such as:

{{ range services }}
{{ range service .Name }}
  {{ .Address }}
{{ end }}{{ end }}

During the first pass, Consul Template does not know any of the services in Consul, so it has to perform a query. When those results are returned, the inner-loop is then evaluated with that result, potentially creating more queries and watches.

Because of this implementation, template functions need a default value that is an acceptable parameter to a range function (or similar), but does not actually execute the inner loop (which would cause a panic). This is important to mention because complex templates must account for the "empty" case. For example, the following will not work:

{{ with index (service "foo") 0 }}
# ...
{{ end }}

This will raise an error like:

<index $services 0>: error calling index: index out of range: 0

That is because, during the first evaluation of the template, the service key is returning an empty slice. You can account for this in your template like so:

{{ with service "foo" }}
{{ with index . 0 }}
{{ .Node }}{{ end }}{{ end }}

This will still add the dependency to the list of watches, but will not evaluate the inner-if, avoiding the out-of-index error.

Debugging

Consul Template can print verbose debugging output. To set the log level for Consul Template, use the -log-level flag:

$ consul-template -log-level info ...
<timestamp> [INFO] (cli) received redis from Watcher
<timestamp> [INFO] (cli) invoking Runner
# ...

You can also specify the level as debug:

$ consul-template -log-level debug ...
<timestamp> [DEBUG] (cli) creating Runner
<timestamp> [DEBUG] (cli) creating Consul API client
<timestamp> [DEBUG] (cli) creating Watcher
<timestamp> [DEBUG] (cli) looping for data
<timestamp> [DEBUG] (watcher) starting watch
<timestamp> [DEBUG] (watcher) all pollers have started, waiting for finish
<timestamp> [DEBUG] (redis) starting poll
<timestamp> [DEBUG] (service redis) querying Consul with &{...}
<timestamp> [DEBUG] (service redis) Consul returned 2 services
<timestamp> [DEBUG] (redis) writing data to channel
<timestamp> [DEBUG] (redis) starting poll
<timestamp> [INFO] (cli) received redis from Watcher
<timestamp> [INFO] (cli) invoking Runner
<timestamp> [DEBUG] (service redis) querying Consul with &{...}
# ...

FAQ

Q: How is this different than confd?
A: The answer is simple: Service Discovery as a first class citizen. You are also encouraged to read this Pull Request on the project for more background information. We think confd is a great project, but Consul Template fills a missing gap. Additionally, Consul Template has first class integration with Vault, making it easy to incorporate secret material like database credentials or API tokens into configuration files.

Q: How is this different than Puppet/Chef/Ansible/Salt?
A: Configuration management tools are designed to be used in unison with Consul Template. Instead of rendering a stale configuration file, use your configuration management software to render a dynamic template that will be populated by Consul.

Q: How does compatibility with Consul look like?
A: The following table shows the compatibility of Consul Template with Consul versions:

Consul v1.16 Consul v1.17 Consul v1.18 Consul v1.16+ent Consul v1.17+ent
CT v0.37
CT v0.36 N/A N/A
CT v0.35 N/A N/A
CT v0.34 N/A N/A

N/A = ENT tests were not supported before this version

Contributing

To build and install Consul-Template locally, you will need to install Go.

Clone the repository:

$ git clone https://github.com/hashicorp/consul-template.git

To compile the consul-template binary for your local machine:

$ make dev

This will compile the consul-template binary into bin/consul-template as well as your $GOPATH and run the test suite.

If you want to run the tests, first install consul, nomad and vault locally, then:

$ make test

Or to run a specific test in the suite:

go test ./... -run SomeTestFunction_name

consul-template's People

Contributors

angrycub avatar armon avatar banks avatar dadgar avatar deblasis avatar dependabot[bot] avatar dhuckins avatar divyaac avatar eikenb avatar findkim avatar gmr avatar hc-github-team-consul-ecosystem avatar jasonodonnell avatar jm96441n avatar jmurret avatar jsanant avatar kevinschoonover avatar kkavish avatar kyhavlov avatar lkysow avatar nathancoleman avatar pearkes avatar roncodingenthusiast avatar ryanuber avatar sethvargo avatar slackpad avatar stefreak avatar tgross avatar thevilledev avatar williambailey avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

consul-template's Issues

Specifying file permissions

If the output file does not exist on disk, Consul Template will create a new file with the default permissions. For sensitive information, this may not be the desired behavior. This change would add an optional 4th parameter: mode, which is the octal representation of the file permissions on disk.

consul-template \
  -template "in.ctmpl:out.txt:some command:644"

or in the Config:

template {
  Source  = "in.ctmpl"
  Destination = "out.txt"
  Command = "some command"
  FilePermissions = "644"
}

using ".Key" on "range keyPrefix" produces full key path rather than child key

This:

{{range keyPrefix "haproxy/tcp/frontend"}}
{{.Key}} {{.Value}}{{end}}

produces this:

haproxy/tcp/frontend/mysql-offline/acl/mysql_offline_dead nbsrv(offline) lt 1
haproxy/tcp/frontend/mysql-offline/acl/
haproxy/tcp/frontend/mysql-offline/bind *:3307
haproxy/tcp/frontend/mysql-offline/default_backend offline
haproxy/tcp/frontend/mysql-offline/maxconn 2000

while expecting something along:

bind *:3307
default_backend offline
maxconn 2000

It would also be useful if there was no recursion down subdirs (or at least an option to avoid that), so that one could use nesting (think multiple acl lines in haproxy config, differing between services).

Template blocks are merged when using a configuration directory

If you pass the -template argument multiple times on the CLI, it will keep track of each one and update it as necessary. If you create a single .hcl file with multiple template blocks, the same result happens. However, if you pass consul-template a directory containing multiple .hcl files, each file with a template block, then consul-template seems to merge those template blocks and only render one.

After speaking with armon on IRC, this appears to be a bug.

README service dependency documentation is unclear around DataCenter usage

The following block of text in the examples lead me to believe that the Service Dependency would support a range across all DCs.

The tag, datacenter and port attributes are optional. To query all nodes in the "webapp" service (regardless of tag, datacenter, etc)

The documentation should be updated to make it more clear that without the datacenter qualifier the dependency will instead default to the current DC.

Dependency graph not properly built when nested inside dynamic conditional

We were attempting to dynamically add in ranges per DC in a somewhat "creative" solution to allow a single template to service a dynamic number of DCs. We are massing the output from the /v1/catalog/datacenters endpoint into a JSON file to indicate the DC's health/addressability of the DC and attempting to use this file in conditionals to add in the range or not.

What we've observed is the dependency graph does not get properly generated when the range is inside a conditional block that is reliant upon the parsed JSON.

Note: I don't know how feasible it would be, but allowing the creation of a dependency against a DC that currently does not exist would fix this issue by simply removing the need to do the conditional in the first place.

Given the contents of dcs.json are:

{
  "us-east-1":true, 
  "us-west-2":false
}

Fails when conditional is based solely on the dynamic value via the file pipeline/parsed JSON

{{ with $dcs := file "dcs.json" | replaceAll "-" "_" | parseJSON }}
  {{if $dcs.us_east_1}}
    {{ range service "consul@us-east-1"}}node {{.Node}}{{end}}
  {{end}}
  {{if $dcs.us_west_2}}
    us-west-2 SHOULDN'T APPEAR
  {{end}}
{{end}}

Works "properly" because the dependency was created outside conditional, however not really an option because adding in a range to a non-addressable DC abends the consul-template

{{ range service "consul@us-east-1"}}{{end}}
{{ with $dcs := file "dcs.json" | replaceAll "-" "_" | parseJSON }}
  {{if $dcs.us_east_1}}
    {{ range service "consul@us-east-1"}}node {{.Node}}{{end}}
  {{end}}
  {{if $dcs.us_west_2}}
    us-west-2 SHOULDN'T APPEAR
  {{end}}
{{end}}

Works properly with non-dynamic conditional

{{ with $dcs := true }}
  {{if $dcs }}
    {{ range service "consul@us-east-1"}}node {{.Node}}
    {{end}}
  {{end}}
{{end}}

Specify configuration directory

Option to specify config directory instead of a single file.

-config=/etc/consul-template/conf/ -consul=127.0.0.1:8500

With this option, we might require that the consul address be specified on the command line and ignore the key in the files since I think consul template can currently only maintain a connection to 1 client at a time.

Panic if `-consul` is set to an agent that isn't joined yet

I got this panic, I'm still trying to figure out what config is causing it:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x0 pc=0x493e00]

goroutine 28 [running]:
runtime.panic(0x75c060, 0x98c8b3)
    /usr/local/Cellar/go/1.3.3/libexec/src/pkg/runtime/panic.c:279 +0xf5
github.com/hashicorp/consul-template/util.(*WatchData).poll(0xc20808ae40, 0xc2080046c0)
    /Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/util/watcher.go:160 +0x350
github.com/hashicorp/consul-template/util.func·002(0xc20808ae40)
    /Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/util/watcher.go:85 +0x64
created by github.com/hashicorp/consul-template/util.(*Watcher).Watch
    /Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/util/watcher.go:86 +0x392

Unfriendly Error When No Template Functions Are Present

If you provide a template with just static text consul-template returns this error:
2014/10/22 22:51:57 DEBUG creating Runner
2014/10/22 22:51:57 DEBUG creating Consul API client
2014/10/22 22:51:57 DEBUG creating Watcher
2014/10/22 22:51:57 [ERR] watcher: must supply at least one Dependency

This would probably never happen in practice but it's a difficult error to debug.

Node status impacts service status

Hi,

I'm using consul-template (with {{range service "api"}}) to generate my HAProxy configuration and everything is working fine.

I recently added node health checks. Now, when there is a memory warning on the node, the "api" service from that node gets dropped from the HAProxy configuration.

It looks like - and this is probably by design - when some checks on the node warn or fail, the services on the machine are considered "not passing" too.

In my use case, I don't want a memory warning on the node dropping the services from HAProxy. And I can't use the "any" option because I don't want nodes having critical states to show up in my HAProxy config file.

Is there a workaround for this? I checked to see if I could use conditionals in the templates, but the Status is not exposed (and it's not the service's status that fails anyway).

Example node health:

$ curl localhost:8500/v1/health/node/api1

[
    {
        "CheckID": "mem",
        "Name": "mem",
        "Node": "api1",
        "Notes": "",
        "Output": "",
        "ServiceID": "",
        "ServiceName": "",
        "Status": "warning"
    },
    {
        "CheckID": "service:api",
        "Name": "Service 'api' check",
        "Node": "api1",
        "Notes": "",
        "Output": "",
        "ServiceID": "api",
        "ServiceName": "api",
        "Status": "passing"
    }
]

Provide helper for decoding JSON

Since it is fairly common for keys to store JSON, it would be great to provide a json function that would automatically decode the JSON into a usable struct:

Potential examples:

{{with whatever := json key "service/consul-replicate/status@sfo1"}}
{{with status := key "service/consul-replicate/status@sfo1"}}
{{with parsed := json status}}
UpdatedAt: {{.LastReplicated}}
{{end}}
{{end}}

Investigate partial file writes

There is a really weird bug I have encountered sporadically when testing Consul Template manually. It seems that sometimes the file is only partially written and that data at the end of the file is persisted. I believe this must have something to do with file.sync() or file.close().

colons not allowed in key?

I get the error "invalid key dependency format" when I try to output a key containing a colon: {{key "myapp/vagrant-ubuntu-trusty-64:loving_elion:80/cname"}}

consul itself doesn't seem to have a problem with the colon. The service-id's generated by registrator contain colons, so working around this will be cumbersome.

Remember LastIndex for Consul keys

In case consul-template process is restarted it would regenerate all configs and invoke defined commands which would needlessly restart services etc...
If consul-template would track all consul kv/service modifyIndex used in teplates and dump them so some statefile each time blocking call returns with modified index of some key, and load this file on startup if it exists , that would be amazing :)

Ignore empty keys

If consul returns a key which is "empty" for ls, we should not include it in the set (refs #53)

Documentation for use with Docker

How would I go about running consul-template inside a container along side my python app, for example.

Use supervisor, or?

Thanks

can't use variables to query range

I'm trying to grab a range of keys using {{range ls "path"}} and then iterate over those, using something like: {{range ls "path/$key"}} but can't find a way to make this work.
I've gone as far as create the following:

{{range ls "haproxy/tcp/frontend"}}
{{$y := .Key | printf "haproxy/tcp/frontend/%s" }}
y = {{$y}}
{{range ls $y}}
{{.Key}}
{{end}}
{{end}}

But that doesn't work either (though $y does contain the correct path). Nesting of {{}} doesn't work either.
Without this capability, use of ranges is quite limited. If there's a way to achieve this that I'm missing, I'd love to know how!

different command per same template

I have config files used by some apps and consul-template can generate them just fine (especially after the #39 - tnx a lot for this).

My current issue is that i can only take one action then template is rendered - full restart of the relevant app. But I would like to go further, depending on what has changed in template i want to take different actions. I know i can split one config template into multiple templates where each one will make different action if rendered. But i was wondering if its technically possible to do this with one template and specify action command next to template functions for example smthing like this:

{{key "service/redis/maxconns@east-aws" command:"/some/command"}}
{{key "service/redis/minconns@east-aws" command:"/another/command"}}

in case more that one keys/services with different command has changed , all command would be executed.

{{range ls ...}} adds extra empty key entries when dirs are present

I have the following key structure:

mysql-offline => ""
mysql-online => ""
mysql-offline/
mysql-online/

The following template:

{{range ls "haproxy/tcp/frontend@chidc2"}}
|-{{.Key}}-|
{{end}}

Generates the following output:


|-mysql-offline-|

|-mysql-online-|

|--|

and if further dirs are added in that path, additional "empty keys" appear as well. The expected behavior is that no empty keys will appear at all.

Raise an exception if empty data is returned?

Let's say I have a template like this:

backends{{range service "consul"}}
    server {{.Address}} {{.Node}}{{end}}

And for some reason, none of the consul nodes are healthy. The API would query and consulapi would return an empty service list. The rendered template would be:

backends

This could, theoretically, cause a restart of the service and everything could die 😦. What is the right behavior in this case? Should we push that off to the user?

/cc @mitchellh @armon

Explore and test "file" cases

Need to investigate the following cases and write tests to ensure Consul Template behaves correctly:

  • Containing folder for output file does not exist
  • Output file does not exist
  • Output file exists but is not writable by the current user (perms and locked)
  • Output file retains original permissions if they exist

Documentation for the file helper has an error

The inclusion of the parentheses in the following example for file is incorrect

{{(with $d := key "user/info" | parseJSON)}}{{$d.name}}{{end}}

that results in

unexpected in parenthesized pipeline

Basic string manipulations

A request came up in IRC on #consul for some basic ToUpper(), ToLower(), Title(), etc. functions to be available from within a template. Looking at go's funcMap in the template package, this seems like it might be pretty easy to do and could be useful.

Query by tag

I don't see it mentioned in the docs, so this would be a new feature request
{{service "api.*"}}

Specify drive letters for Windows

I have the misfortune to be running windows in production.

This syntax works fine:

C:\Users\craigs>c:\consul\consul-template.exe -consul=127.0.0.1:8500 -template="\chef-solo\cache\node.ctmpl:\chef-solo\continuity.json"

However, if I need to specify a template on a drive other than the boot drive, consul-template choaks (is this because of the surfeit of colons?):

C:\Users\craigs>c:\consul\consul-template.exe -consul=127.0.0.1:8500 -template="d:\chef-solo\cache\node.ctmpl:d:\chef-solo\continuity.json"

invalid value "d:\chef-solo\cache\node.ctmpl:d:\chef-solo\continuity.json"
for flag -template: invalid template declaration format

using stdin/stdout for templates?

Is it possible to read the template from stdin and/or write the output to stdout rather than from a file? It would be nice to avoid managing extraneous temporary files.

Using stdin/stdout without the -once flag would be nonsensical.

the -dry flag almost works for standard output, but it adds an extra leading line.

P.S. Do you have another mechanism for these sorts of questions, such as a mailing list? This is the third time I've (ab)used the issue tracker for usage questions like this.

Storing ctmpl in consul keys

For some configs, which change frequently, I believe it would be useful to store the ctmpl file in consul itself in k/v

Empty Key/Value pair with `range ls`

With the following KV data:

$ curl localhost:5200/v1/kv/service/app/config?recurse
[
  {
    "CreateIndex": 2592,
    "ModifyIndex": 2680,
    "LockIndex": 0,
    "Key": "service/app/config/key1",
    "Flags": 0,
    "Value": "aGVsbG8="
  },
  {
    "CreateIndex": 2591,
    "ModifyIndex": 2681,
    "LockIndex": 0,
    "Key": "service/app/config/key2",
    "Flags": 0,
    "Value": "d29ybGQ="
  },
  {
    "CreateIndex": 2587,
    "ModifyIndex": 2587,
    "LockIndex": 0,
    "Key": "service/app/config/",
    "Flags": 0,
    "Value": null
  }
]

And this template:

{{ range ls "service/app/config" }}{{ .Key | toUpper }} = "{{ .Value}}"
{{end}}

I get this output:

KEY1 = "hello"
KEY2 = "world"
 = ""

Desired output:

KEY1 = "hello"
KEY2 = "world"

Option to query all services, not just passing ones

Could we add support for allowing the user to specify whether or not to return only passing nodes. Perhaps we could inherit the syntax the Consul api uses and allow the user to add '?passing' to the service query.

{{ service "data-service-api@dc1:8080?passing" }}

Seems like a relatively small code change, but it would change the default behavior.

  • Add regex match for '?passing' to ParseServiceDependency()
  • Add Passing param to ServiceDependency struct
  • Replace hardcoded true with ServiceDependency struct property in api call in (d *ServiceDependency) Fetch

Error if /tmp and target are on different file systems

I had a source of /etc/hosts-template to destination of /etc/hosts
the problem is that my /tmp is on a different volume, so I received an error saying
that cannot rename /tmp/201230FILENAME to /etc/hosts because they are not on the same file system.

As a work around, i set the destination in the tmp folder, and use the command to overwrite the file i want.
I think that's probably what consul-template should do to begin with...

Add support for querying all services

It would be great if the service API function could be used to query all services (similar to the new nodes functionality).

I may submit a PR for this if I get some time this weekend.

Documentation of dynamic filenames as a workaround for #64

I think I've got my workaround for bug #64 and the limitation that filenames cannot be dynamic. I should probably turn this into a documentation PR once things look good. I'm going to write it in that style. My enhancement request is at the bottom.


Examples

Multiple Nginx config files

Nginx is a popular web server, also popular as a proxy. For this example, we are going to create multiple nginx site files, where the site name is listed in a consul key namespace.

For this example to work, you must have your consul-template configured to read it's configuration from a directory rather than a file.

Our strategy is to create a two layer template. Our template generates templates. It is possible to do this by using consul-template for both the outer and inner templates: you use the printf function to write out lines containing {{}}, but this becomes very awkward quite quickly. So instead for this example we'll use a simple sed script to generate the outer layer.

webapp-nginx.conf.outer-template:

upstream %SERVICE% {
{{range service "%SERVICE%"}}
  server {{ .Address }}:{{ .Port }};
{{end}}
}
server {
  listen 80;
  server_name {{key "%SERVICE%/cname"}};
  location / {
    proxy_pass http://%SERVICE%;
  }
}

We'll use consul-template to dynamically generate and trigger a script that will run sed on this file multiple times, once for each site.

Note: in this example, all file paths are elided for simplicity.

webapp-nginx.sh.template:

#!/bin/sh

{{ range ls "webapp" }}
sed -e "s!%SERVICE%!{{ .Key }}!g" -e "s!%ID%!{{ .Value }}!g" < webapp-nginx.conf.outer-template > "{{ .Key }}.conf.template"
{{ end }}

We also use consul-template to dynamically generate a config file for consul-template to process the new templates.

webapp-nginx.consul-template.cfg.template:

{{ range ls "webapp" }}
template {
  source = "{{ .Key }}.conf.templ"
  destination = "{{ .Key }}.conf"
  command = "service nginx reload"
}
{{ end }}

And finally the outer configuration file:

webapp-nginx.cfg:

template {
  source = "webapp-nginx.sh.tmplate"
  destination = "webapp-nginx.sh"
  command = "sh webapp-nginx.sh"
}

template {
  source = "/opt/plugins/webapp-nginx/webapp-nginx.consul-template.cfg.tmpl"
  destination = "/opt/consul-template/config/webapp-nginx.gen.cfg"
}

The big thing missing from the above example that I have in my system is the line service consul-template restart at the bottom of webapp-nginx.sh.template. That's there to force consul-template to reload it's configuration. But you'll notice that there are actually 2 places that should trigger a reload: the execution of webapp-nginx.sh and the writing of webapp-nginx.gen.cfg. Right now I assume that webapp-nginx.gen.cfg finishes first. A fairly safe assumption, but a nasty race if it doesn't.

If consul-template supported the HUP signal to safely reload it's configuration, I could send HUP in both places and it wouldn't matter which finished first.

atomicWrite: Error when tmp is on different device

 In a Linux setup, where /tmp is mounted on a different device than the destination file (/etc/haproxy/haproxy.cfg in the example) I get the following error:

2014/11/06 18:19:15 [ERR] rename /tmp/033905806 /etc/haproxy/haproxy.cfg: invalid cross-device link

os.Rename(), which is used at the end of runner.go/atomicWrite(), line 372, can not rename across devices on Linux, see also https://groups.google.com/forum/#!topic/golang-dev/5w7Jmg_iCJQ

Unfortunatly, I have no immediate idea how to fix this.

index on service

Suppose I would like to get the IP address of any consul agent. I would expect something like this to work:

 {{ index (service "consul") 0 }}

What am I doing wrong? Thanks.

Allow specifying a -retry interval

By default, consul-template uses a hard-coded wait time of 5 seconds when a query with Consul fails. It would be nice if this was configurable:

$ consul-template -retry 30s

computed keys not working inside service loop unless used elsewhere

using consul-template v0.3.0

this template:

{{range service "consul"}}
   a {{printf "%s/%s/foo" .Name .Address}}
   b {{key (printf "%s/%s/foo" .Name .Address) }}
{{end}}

produces:

   a consul/172.17.0.2/foo
   b 

yet

{{range service "consul"}}
   a {{printf "%s/%s/foo" .Name .Address}}
   b {{key (printf "%s/%s/foo" .Name .Address) }}
{{end}}
   c {{key "consul/172.17.0.2/foo"}}

produces

   a consul/172.17.0.2/foo
   b bar1

   c bar1

I understand this is a hard problem: b is dependent on both the service and the key. However, adding arbitrary keys per service is probably a common use case. Is there a better way to accomplish what I'm trying to do?

Explore

There is a really weird bug I have encountered sporadically when testing Consul Template manually. It seems that sometimes the file is only partially written and that data at the end of the file is persisted. I believe this must have something to do with file.sync() or file.close().

Error handling on command execution is not intuitive

I know this is subjective but I was surprised to see that consul-template exited with an error when the specified post command exited with an error.
In the command line section of the ReadMe, there is the following example:

consul-template \
  -consul my.consul.internal:6124 \
  -template "/tmp/nginx.ctmpl:/var/nginx/nginx.conf:service nginx restart" \
  -template "/tmp/redis.ctmpl:/var/redis/redis.conf:service redis restart" \
  -template "/tmp/haproxy.ctmpl:/var/haproxy/haproxy.conf"

If the nginx restart command above exits with an error which it will if an invalid piece of config is pushed into consul then consul-template will exit with an error. There is no way to recover other than updating the config in consul then restarting consul-template on all hosts.
Is there a particular reason consul exits in this case? I think ideally this could be configured?
At a minimum it should be documented?

FYI I was able to work around this by appending "|| true" to the command

Run as background daemon

I don't seem to get consul-template running as a background daemon. Is there a way to achieve this?

Runtime exception with incorrect config directory path

Given the path to a directory that does not exist, Consul Template crashes:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x40aaa4]
goroutine 16 [running]:
runtime.panic(0x761e80, 0x9958b3)
/usr/local/Cellar/go/1.3.3/libexec/src/pkg/runtime/panic.c:279 +0xf5
main.func·002(0x7fffffd84f5c, 0x19, 0x0, 0x0, 0x7fd80c2f9818, 0xc208010cc0, 0x0, 0x0)
/Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/cli.go:282 +0x64
path/filepath.Walk(0x7fffffd84f5c, 0x19, 0x7fd80c15ac60, 0x0, 0x0)
/usr/local/Cellar/go/1.3.3/libexec/src/pkg/path/filepath/path.go:388 +0xa9
main.(*CLI).BuildConfig(0xc2080404c0, 0xc2080041e0, 0x7fffffd84f5c, 0x19, 0x0, 0x0)
/Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/cli.go:294 +0x1c6
main.(*CLI).Run(0xc2080404c0, 0xc20800e000, 0x5, 0x5, 0x14)
/Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/cli.go:102 +0x827
main.main()
/Users/sethvargo/Development/go/src/github.com/hashicorp/consul-template/main.go:12 +0xc7

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.