# Consul::Templaterb
This GEM is both a library and an executable that allows to generate files
using data from Consul (Discovery and Key/Value Store) easily using ruby's
erb templates. It also support launching programs and babysitting processes
when rendering the files, thus notifying programs when data do change.
It is intended for user accustomed to expressiveness or Ruby templating (ERB),
allowing for more flexibility and than Go Templating.
It also allow to use all of ruby language, especially usefull for generating
files in several formats (JSON, XML) where text substitutions are hard to get
right.
It also focuses on good performance and lightweight usage of bandwidth,
especially for very large clusters and watching lots of services.
For complicated rendering of templates and large Consul Clusters, it usually
render faster with a more predictible way the template than the original
consul-template.
## Differences with HashiCorp's consul-template
[Hashicorp's Consul Template](https://github.com/hashicorp/consul-template)
inspired strongly the creation of this GEM and this executable wants
to achieve better results in some use cases, especially for very large
Consul clusters with many nodes and servers.
consul-template has more features regarding Consul support (for instance, it
has support for Hashicorp's Vault), but consul-templaterb focuses on getting
more power with the generation of templates and more performance.
Consul Template uses Go templates which is very limited in its set of
features is quite limited: it is complicated to sort, apply real transformations
using code and even interact with the OS (ex: get the current date, format
timestamps...).
The sort feature for instances allow you to create predictible output (i.e: meaning
that the order of nodes is predictible), thus it might offer better performance
since the reload of processes if happening ONLY when the files are binary
different. Thus, if using consul-templaterb, you will reload less your haproxy or
load-balancer than you would do with consul-template.
Compared to consul-template, consul-templaterb offers the following features:
* Hot-Reload of template files
* Bandwith limitation per endpoint (will soon support dynamic bandwith limiter)
* Supports baby sitting of multiple processes
* Supports all Ruby features (ex: base64, real JSON/XML generation...)
* Information about bandwidth
The executable supports close semantics to Consul template, it also supports
commands when files are modified and babysitting of multiple processes with
ability to send signals to those processes whenever the files do change.
## Installation
You might either use the executable direcly OR use this GEM as a library by
adding this line to your application's Gemfile:
```ruby
gem 'consul-templaterb'
```
And then execute:
```shell
$ bundle
[...]
```
Or install it yourself as:
```shell
$ gem install consul-templaterb
[...]
```
If you simply want to use the executable on your favorite linux distribution, you
have to install first: ruby and ruby dev.
### Quick install on Ubuntu
```shell
sudo apt-get install ruby ruby-dev && sudo gem install consul-templaterb
```
You can now use it directly using the binary `consul-templaterb` in your path
## Usage of consul-templaterb
### Show help
```shell
$ consul-templaterb --help
USAGE: bin/consul-templaterb [[options]]
-h, --help Show help
-v, --version Show Version
-c, --consul-addr=
Address of Consul, eg: http://locahost:8500
--consul-token= Use a token to connect to Consul
-w, --wait= Wait at least n seconds before each template generation
-r, --retry-delay= Min Retry delay on Error/Missing Consul Index
-k, --hot-reload= Control hot reload behaviour, one of :[die (kill daemon on hot reload failure), keep (on error, keep running), disable (hot reload disabled)]
-K, --sig-term=kill_signal Signal to sent to next --exec command on kill, default=#{cur_sig_term}
-R, --sig-reload=reload_signal Signal to sent to next --exec command on reload (NONE supported), default=#{cur_sig_reload}
-e, --exec= Execute the following command
-d, --debug-network-usage Debug the network usage
-t erb_file:[output]:[command], Add a erb template, its output and optional reload command
--template
--once Do not run the process as a daemon
```
### Generate multiple templates
In the same way as consul-template, consul-templaterb supports multiple templates and executing
commands when the files do change. The parameter `--template ::[reload_command]` works
in the following way:
* ERB : the ERB file to use as a template
* DEST: the destination file
* reload_command: optional shell command executed whenever the file has been modified
The argument can be specified multiple times, ex:
Example of usage:
```shell
$ consul-templaterb \\
--template "samples/ha_proxy.cfg.erb:/opt/haproxy/etc/haproxy.cfg:sudo service haproxy reload"
--template "samples/consul_template.erb:consul-summary.txt"
```
### Process management and signalisation of configuration files
With the --exec argument (can be specified multiple times), consul-templaterb will launch
the process specified when all templates have been generated and will send a reload signal
if the content of any of the files do change (the signal will be sent atomically however,
meaning that if 2 results of templates are modified at the same time, the signal will be
sent only once (it is helpful for instance if your app is using several configurations
files that must be consistent all together).
Signals can be customized per process. Two signals are supported with options --sig-reload and
--sig-term. When the option is added, the next --exec options to start a process will use the
given signal. By default, HUP will be sent to reload events (you can use NONE to avoid sending any
reload signal), TERM will be used when leaving consul-templaterb.
### Bandwidth limitation
This is actually the original reason for the creation of this GEM: on Criteo's large clusters,
consul-template generated several hundreds of Mb/s to the Consul-Agent which also
generated several hundreds of Mb/s with the Consul servers.
By design, the GEM supports limiting the number of requests per endpoints (see code in
`bin/consul-templaterb` file). It avoids using too much network to fetch data from Consul
in large Consul Clusters (especially when watching lots of files).
The limitation is currently static, but fair dynamic bandwidth allocation will allow to limit
the bandwidth used to get information for all services by capping the global bandwidth used
by consul-templaterb.
### Samples
Have a look into the [samples/](samples/) directory to browse example files.
If you want to test it quickly, you might try with (assuming you consul agent is listening on
http://localhost:8500):
```
$ be bin/consul-templaterb -c 'http://localhost:8500' samples/*.html.erb
```
It will generate a full website in samples/ directory with lots of Consul information ready to
use (website updated automagically when values to change).
## Template development
Here are the various functions you might use in your templates.
For each function, mandatory arguments are specified at the begining while optional ones are marked with `[]`.
Most of them support the optional dc attribute to access data from another datacenter. If the `dc`
attribute is not specified, the function will output data from the current datacenter.
To ease template development, `consul-templaterb` supports HOT reload of templates, thus it is possible to
develop the templates interactivelly. While developping, it is possible to use the switch `--hot-reload=keep`,
thus the application will display a warning if the template is invalid and won't stop
(`--hot-reload=die` is the default, thus if the hot-reloaded template has issue, the application will die).
### datacenters()
[Get the list of datacenters as string array](https://www.consul.io/api/catalog.html#list-datacenters).
### services([dc: datacenter], [tag: tagToFilterWith])
[List the services matching the optional tag filter](https://www.consul.io/api/catalog.html#list-services),
if tag is not specified, will match all the services. Note that this endpoint performs client side tag
filtering for services to ease templates development since this feature is not available on Consul's endpoint.
### service(serviceName, [dc: datacenter], [tag: tagToFilterWith], [passing: true])
[List the instances](https://www.consul.io/api/health.html#list-nodes-for-service) of a service having the given
optional tag. If no tag is specified, will return all instances of service. By default, it will return all the
well services that are passing or not. An optional argument passing might be used to retrieve only passing instances.
### nodes([dc: datacenter])
[List all the nodes of selected datacenter](https://www.consul.io/api/catalog.html#list-nodes). No filtering is
applied.
### node(nodeNameOrId, [dc: datacenter])
[List all the services of a given Node](https://www.consul.io/api/catalog.html#list-services-for-node) using its
name or its ID. If DC is specified, will lookup for given node in another datacenter.
### checks_for_service(name, dc: nil, passing: false, tag: nil)
[Find all the checks](https://www.consul.io/api/health.html#list-checks-for-service) of a given service.
### kv(name = nil, dc: nil, keys: nil, recurse: false)
[Read keys from KV Store](https://www.consul.io/api/kv.html#read-key). It can be used for both listing the keys and
getting the values. See the file in samples [keys.html.erb](samples/keys.html.erb) for a working example.
### agent_metrics()
[Get the metrics of Consul Agent](https://www.consul.io/api/agent.html#view-metrics). Since this endpoint does
not support blocking queries, data will be refreshed every few seconds, but will not use blocking queries
mechanism.
### agent_self()
[Get the configuration of Consul Agent](https://www.consul.io/api/agent.html#read-configuration). Since this
endpoint does not support blocking queries, data will be refreshed every few seconds, but will not use blocking
queries mechanism.
### render_file RELATIVE_PATH_TO_ERB_FILE
This allow to include a template file into another one. Beware, it does not check for infinite recursion!
The template can be either a static file either another template. The file has to be a valid template, but
can also be raw text (if it is a valid template.
Example:
```erb
<%= render_file 'header.html.erb' %>
```
## Development
We recommend using bundle using `bundle install`, you can now run `bundle exec bin/consul-templaterb`.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the
version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version,
push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Known bugs
Here are the known bugs of the application:
* [ ] On Mac OS X, with many services, consul-templaterb sometimes crashes when watching lots of changes (more than 2k
watches) at the same time. This bug is a race condition, probably in `em-http-request`. Only visible on very large
clusters.
* [ ] render_file might create an infinite recusion if a template includes itself indirectly.
## TODO
* [ ] Hashi's Vault support
* [ ] Implement automatic dynamic rate limit
* [ ] More samples: apache, nginx, full website displaying consul information...
* [ ] Optimize rendering speed at startup: an iteration is done very second by default, but it would be possible to speed
up rendering by iterating with higher frequency until the first write of result has been performed.
* [ ] Allow to tune bandwith using a simple config file (while it should not be necessary for 90% of use-cases)
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/criteo/consul-templaterb.
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the
[Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the Apache v2 license. See LICENSE.txt file.