consul-templaterb 1.25.2 → 1.27.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -28,6 +28,9 @@ options = {
28
28
  },
29
29
  base_url: ENV['VAULT_ADDR'] || 'http://localhost:8200',
30
30
  token: ENV['VAULT_TOKEN'] || nil,
31
+ tls_cert_chain: ENV['VAULT_CLIENT_CERT'] || nil,
32
+ tls_private_key: ENV['VAULT_CLIENT_KEY'] || nil,
33
+ tls_verify_peer: true,
31
34
  max_consecutive_errors_on_endpoint: 10, # Stop program after n consecutive failures on same endpoint
32
35
  fail_fast_errors: nil, # fail fast the program if endpoint was never success
33
36
  token_renew: true,
@@ -48,6 +51,9 @@ options = {
48
51
  },
49
52
  base_url: ENV['CONSUL_HTTP_ADDR'] || 'http://localhost:8500',
50
53
  token: ENV['CONSUL_HTTP_TOKEN'] || nil,
54
+ tls_cert_chain: ENV['CONSUL_CLIENT_CERT'] || nil,
55
+ tls_private_key: ENV['CONSUL_CLIENT_KEY'] || nil,
56
+ tls_verify_peer: true,
51
57
  max_consecutive_errors_on_endpoint: 10, # Stop program after n consecutive failures on same endpoint
52
58
  fail_fast_errors: nil, # fail fast the program if endpoint was never success
53
59
  retry_duration: 10, # On error, retry after n seconds
@@ -122,6 +128,18 @@ optparse = OptionParser.new do |opts|
122
128
  options[:consul][:base_url] = consul_url
123
129
  end
124
130
 
131
+ opts.on('--consul-cert-chain=<path/to/cert_chain>', String, 'Path to Consul TLS client certificate chain to use') do |consul_client_cert|
132
+ options[:consul][:tls_cert_chain] = consul_client_cert
133
+ end
134
+
135
+ opts.on('--consul-private-key=<path/to/private_key>', String, 'Path to Consul TLS client private key to use') do |consul_client_key|
136
+ options[:consul][:tls_private_key] = consul_client_key
137
+ end
138
+
139
+ opts.on('--skip-consul-verify-tls', 'Skip verifying Consul TLS via certificate authority (DANGEROUS)') do
140
+ options[:consul][:tls_verify_peer] = false
141
+ end
142
+
125
143
  opts.on('-l', '--log-level=<log_level>', String, "Log level, default=info, any of #{::Consul::Async::Debug.levels.join('|')}") do |log_level|
126
144
  ::Consul::Async::Debug.level = log_level
127
145
  end
@@ -134,6 +152,18 @@ optparse = OptionParser.new do |opts|
134
152
  options[:vault][:base_url] = vault_url
135
153
  end
136
154
 
155
+ opts.on('--vault-cert-chain=<path/to/cert_chain>', String, 'Path to Vault TLS client certificate chain to use') do |vault_client_cert|
156
+ options[:vault][:tls_cert_chain] = vault_client_cert
157
+ end
158
+
159
+ opts.on('--vault-private-key=<path/to/private_key>', String, 'Path to Vault TLS client private key to use') do |vault_client_key|
160
+ options[:vault][:tls_private_key] = vault_client_key
161
+ end
162
+
163
+ opts.on('--skip-vault-verify-tls', 'Skip verifying Vault TLS via certificate authority (DANGEROUS)') do
164
+ options[:vault][:tls_verify_peer] = false
165
+ end
166
+
137
167
  opts.on('-T', '--vault-token=<token>', String, 'Token used to authenticate against vault.') do |vault_token|
138
168
  options[:vault][:token] = vault_token
139
169
  end
@@ -0,0 +1,124 @@
1
+ # Template-based discovery with consul-templaterb
2
+
3
+ Source of article https://medium.com/criteo-labs/template-based-discovery-with-consul-templaterb-8ff88434c457
4
+
5
+ # How and why Criteo built a template-based system for Consul
6
+
7
+ [consul-templaterb](https://github.com/criteo/consul-templaterb/) is an OpenSource Software (OSS) written in Ruby that allows you to very quickly create UIs for Consul or configuration files for your beloved software to interact with Consul. This article describes why we wrote it and why you might be interested in using it.
8
+
9
+ # The context
10
+
11
+ At the end of 2017, Consul started becoming a very important piece in Criteo’s infrastructure as it was responsible for dealing with the discovery of all major business applications at Criteo. At that time, Criteo had more than 25,000 servers on production and some datacenters had more than 5,000 agents per datacenter. At the same time, we started building our Load-Balancer stack based on the Consul state. So it became more and more important to make it efficient, stable and expand it with new features.
12
+
13
+ Criteo wanted to move towards HAProxy for its load-balancing stack, thus we started generating some configuration for HAProxy with all HTTP services with the template-based configuration generator consul-template. However, things were a bit more complicated than expected because the program consul-template was using almost all bandwidth available just to discover all services. Moreover, the system was constantly updating HAProxy configuration.
14
+
15
+ As explained in our first article “[Discovery with Consul at scale](https://medium.com/criteo-labs/discovery-with-consul-at-scale-1d6808202d86)”, Consul includes a notification system to ensure your systems are always up-to-date with the content of the discovery. When this system starts to push too many notifications, on large services, the amount of data sent to all agents can be quite impressive (several megabytes/s for a single service having hundreds of instances).
16
+
17
+ Thus, we started investigating the issue and figured out that it was linked to a race condition in large clusters that caused all listeners of Consul to be notified continuously. This issue was eventually solved by our [Pull-Request #3899](https://github.com/hashicorp/consul/pull/3899) (and quite a few others after that), but the investigation showed that it was quite hard to investigate the root cause of such issues.
18
+
19
+ Therefore, I started a simple tool to watch all the services matching some patterns and report the bandwidth usage, but I also wanted to prove it would be possible to generate all configurations for HAProxy with an optimized tool, thus having a templating mechanism. The [Pull Request #1066](https://github.com/hashicorp/consul-template/pull/1066) to fix the issue on the consul-template itself was not yet considered (and it was hard to have something better), so I decided to implement it in Ruby with asynchronous I/Os (Input/Outputs) because I was very frustrated by the Go templating language used in consul-template and was in love with ERB templates.
20
+
21
+ # The goals
22
+
23
+ From scratch, I wanted to take the various shortcoming of consul-template into account:
24
+
25
+ * The ability to generate high-level code to generate various formats (YAML, JSON, XML): it is dangerous, error-prone and painful to generate JSON or XML content using text templating. People fighting with YAML indentation will tell you that.
26
+ * The ability to use a Turing complete and effective language to perform simple transformation such as sorting (Consul by default has some rules to sort the output, but it might not be predictable, for instance, based on the round-trip between servers).
27
+ * Being able to push real-life optimizations (for instance, we don’t care about being notified immediately when a change occurs, we want to be notified once before dumping the configuration file, not 100 times without taking it into account), based on the type of query performed.
28
+ * Being able to scale nicely with Consul and avoid overloading the cluster for nothing in case of bug (for instance, we use a penalty for services changing too much, we rate-limit the notifications).
29
+ * Being very simple to use, hiding all the complexity to the template creator and using optimizations to reduce the number of calls to Consul, meaning being as purely functional as possible by hiding all I/O operations and taking decisions based on our experience with Consul internals.
30
+ * Be evolutive: When some functions or fields are missing in a consul-template, you have to do a pull request on consul-template to add your new fields/methods, then you can work. It would be nice if any field of endpoints would be supported natively (so new versions of Consul can be used with an old version of our tool).
31
+ * Support for hot-reload of templates, and nice error messages with the precise line when an error occurs, so writing templates should be very fast and efficient.
32
+ * Have most of the consul-template features including babysitting of processes, spawn commands when files do change…
33
+
34
+ # Internals: make your template engine in Ruby
35
+
36
+ ## At startup
37
+
38
+ The engine collects all template sources and computes the destinations. Each template and its destination also register some commands to run when the destination does change. Those objects save the last time the template has been read (to allow hot-reload) and the last binary content associated with them (to detect binary changes).
39
+
40
+ ## The main loop
41
+
42
+ First, the main loop is working on pending events. Those events are in fact the processing of the I/O operations querying the Consul agent. But every second by default, the main loop also evaluates templates which are Ruby code. When this is just regular code, this is evaluated normally, but when the code called is part of the [I/O API](https://github.com/criteo/consul-templaterb/blob/master/TemplateAPI.md), the code does the following:
43
+
44
+ 1. Checks in a registry if this method has been called with the same parameters, if not, create it, mark it as dirty (so the registry knows we are waiting for data from Consul), launch the request asynchronously to Consul, store it in the registry. This will be translated into events that will be run as events in the main loop.
45
+ 2. Return the object stored within the registry. By default, this object returns the empty object or collection according to its type. When the request finally gets a response from Consul, the object is marked as “non-dirty” and the result is put within the registry, so the next evaluation returns the content of the requests.
46
+
47
+ Once all code has been evaluated and the result has been stored in memory, the main loop checks whether the template is using “dirty” objects in the registry. Having at least one dirty object means that some requests are still in flight and we don’t have the result yet, so basically means the template, while evaluated, has been evaluated with fake (empty) data and is not yet ready.
48
+
49
+ However, if the template is not using any dirty object it means we did receive all the data and we can render the template on disk. We first check if our last rendering was different. If the new rendering phase was not different, nothing did change, however, if the data rendered is different, the file is stored to disk and commands might be evaluated (to notify a program to reload for instance).
50
+
51
+ All the I/O are done asynchronously (using [eventmachine](https://github.com/eventmachine/eventmachine)) but performed within the main loop, the one also performing the rendering, so, there is no possibility of threading issues.
52
+
53
+ ![Main loop behavior, in purple, the async fetch, in yellow, the rendering](images/consul-templaterb.png)
54
+
55
+ ## Handling of I/O
56
+
57
+ The Input/Outputs are handled very carefully with regards to what we explained in our previous article “[Be a good Consul client](https://medium.com/criteo-labs/be-a-good-consul-client-5b55160cff7d)” and adds a few tricks:
58
+
59
+ * When a given endpoint is changing too fast with any real change (the data is the same as last call), a penalty is applied, so the endpoint won’t be queried before a few seconds (this allows to work well even with old Consul versions with patch [#3899](https://github.com/hashicorp/consul/pull/3899)).
60
+ * Some endpoints [are configured specifically](https://github.com/criteo/consul-templaterb/blob/master/bin/consul-templaterb#L61) to avoid too many calls (for instance, we don’t consider we add a new DC every 30 seconds).
61
+ * Some specific behavior exists for non-existing service (was causing lots of issues with Prometheus for instance, see [Prometheus PR #3814](https://github.com/prometheus/prometheus/pull/3814)), so we will work on the old Consul version with our [PR #4810](https://github.com/hashicorp/consul/pull/4810).
62
+ * By default, when some errors occur, try to limit the number of calls to avoid increasing the pressure on the cluster.
63
+ * Collect statistics that can be used in your templates. For instance, the [Prometheus exporter uses it to know whether a service is unstable or not](https://github.com/criteo/consul-templaterb/blob/master/samples/metrics.erb#L88).
64
+
65
+ ## The hard part about async I/Os with Ruby
66
+
67
+ This program was my first project dealing with complex code with important performance issues. I first discovered that Ruby async I/O code is really hard and most libraries are not dealing with it properly. Many libraries just lie (by creating threads and pretending to be async). The only library I found handling it correctly was [eventmachine](https://github.com/eventmachine/eventmachine/).
68
+
69
+ While the library is petty mature (the project is quite old, 9 years old), I had lots of issues very quickly. I spent quite some time making it work on my targeted operating systems (Linux, Mac OS, Windows).
70
+
71
+ * On Windows, installation is painful (see https://github.com/criteo/consul-templaterb/#quick-install-on-windows) and due to limitation into the Ruby runtime, it does not support more than 2048 file descriptors with native implementation (while this works with Linux Subsystem on Windows 10).
72
+ * On all Operating Systems, opening lots of HTTP connections concurrently led to crashes (see https://github.com/igrigorik/em-http-request/issues/315, [#604](https://github.com/eventmachine/eventmachine/issues/604), [#770](https://github.com/eventmachine/eventmachine/issues/770), and [#824](https://github.com/eventmachine/eventmachine/issues/824)) that took me lots of time to debug (including native C code debugging), quite frustrating for such mature library (the good news is that eventmachine is now fixed thanks to [consul-templaterb](https://github.com/criteo/consul-templaterb/)).
73
+
74
+ While those problems are now solved and you can use it properly, it took me weeks to solve.
75
+
76
+ Furthermore, the lack of HTTP/2 is a bit worrisome today as more and more systems allow it (it would avoid the lack of file descriptors on very large clusters).
77
+
78
+ ## Some fun features
79
+
80
+ * All templates in the repository are unit tested for each release of consul-templaterb, so all samples are working on your local installation.
81
+ * You can generate lots of formats: [Services in XML](https://github.com/criteo/consul-templaterb/blob/master/samples/consul_template.xml.erb), [JSON](https://github.com/criteo/consul-templaterb/blob/master/samples/consul_template.json.erb), [YAML](https://github.com/criteo/consul-templaterb/blob/master/samples/consul_template.json.erb#L58) using Hash and just dumping in the right format!
82
+ * You can load templates from Consul Key/Value store, so you can change templates dynamically from Consul K/V Store (we use this feature in for our Prometheus configurations for instance as described in “[Mixing Observability with Service Discovery](https://medium.com/criteo-labs/mixing-observability-with-service-discovery-2bb8909e8530)”).
83
+ * You can also query Vault / some JSON APIs very easily.
84
+ * You can have a very high-performance UI (see [Consul-UI](https://github.com/criteo/consul-templaterb/tree/master/samples/consul-ui)) easily for your own use-cases. This UI is generated in real-time as static files and can be served by your favorite webserver (we do use nginx on our side), so it scales indefinitely if you have lots of users.
85
+ * Huge Performance gap with consul-template: in version 1.0 of Consul, consul-template was using 800Mb/s to scrape all services in one of large DCs, consul-templaterb is using less than 100kb/s (800x reduction!) to generate the full UI of Consul and fetch all services in our largest DC now.
86
+ * Good abstraction with impressive performance compared to other projects such as [consult](https://github.com/veracross/consult) (no need to deal with kind of “magic TTL”) or consul-template itself: the APIs are simpler, you have to deal less with Consul specific optimizations and the performance is better.
87
+
88
+ # Usages at Criteo (non-exhaustive)
89
+
90
+ We use this executable for many things at Criteo (see “Inversion of Control with Consul”):
91
+
92
+ * Automatic Alerting / Prometheus configuration
93
+ * [Consul-UI](https://github.com/criteo/consul-templaterb/tree/master/samples/consul-ui)s and its Consul Timeline (keep track of the history of all changes for all services)
94
+ * List of assets per DC/Racks
95
+ * Monitoring of services/racks
96
+ * Ownership enforcement
97
+ * Live generation of configuration for several programs/services
98
+
99
+ # Try it quickly
100
+
101
+ If you don’t want to bother configuring and tuning it, you can try it very quickly with our docker images: In 2 minutes (download included), you have a full scalable UI with Consul, serving static files with nginx and keeping a history of all changes on your services live.
102
+
103
+ ![Consul UI in action, scalable UI with excellent performance](images/consul-ui_001.png)
104
+
105
+ # More on the subject
106
+
107
+ * The [consul-templaterb API](https://github.com/criteo/consul-templaterb/blob/master/TemplateAPI.md) to write templates, with links to real-world examples in the samples directory.
108
+ * [INTERNALS.md](../INTERNALS.md) on Github (describes the objects in the code)
109
+ * Slides at HashiConf ’19: “[Consul Templates on Steroids](https://fr.slideshare.net/PierreSouchay/2019-hashiconf-consultemplaterb)”
110
+ * A video introduction of consul-templaterb:
111
+
112
+ [![Quick presentation of consul-templaterb](images/consul-ui_001.png)](https://youtu.be/zLzrLGLLl4Q)
113
+
114
+ # Other articles from the Discovery team
115
+
116
+ * [Discovery with Consul at scale](https://medium.com/criteo-labs/discovery-with-consul-at-scale-1d6808202d86)
117
+ * [Be a good Consul client](https://medium.com/criteo-labs/be-a-good-consul-client-5b55160cff7d)
118
+ * [Anatomy of a bug: When Consul has too much to deliver for the big day](https://medium.com/criteo-labs/anatomy-of-a-bug-when-consul-has-too-much-to-deliver-for-the-big-day-4904d19a46a4)
119
+ * [Inversion of Control for the Infrastructure with Consul](https://medium.com/criteo-labs/inversion-of-control-for-the-infrastructure-with-consul-b894877b33a4)
120
+ * [Mixing Observability with Service Discovery](https://medium.com/criteo-labs/mixing-observability-with-service-discovery-2bb8909e8530)
121
+
122
+ Pierre Souchay, 2020-03-03
123
+
124
+ Also published on https://medium.com/criteo-labs/template-based-discovery-with-consul-templaterb-8ff88434c457
@@ -0,0 +1,3 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="896px" height="455px" viewBox="-0.5 -0.5 896 455" content="&lt;mxfile host=&quot;www.draw.io&quot; modified=&quot;2020-03-02T13:27:25.463Z&quot; agent=&quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:73.0) Gecko/20100101 Firefox/73.0&quot; etag=&quot;Qxj7PV3jLHrlLdg27esA&quot; version=&quot;12.7.9&quot; type=&quot;device&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;58cdce13-f638-feb5-8d6f-7d28b1aa9fa0&quot;&gt;7VzZkps4FP0aP3YXIDY/9pqpTDI1k04myVNKBtlmgpEL5HY7Xz8SSCwSm21wO4n7IUELAt977rmLZE/A3erlTQzXy/fYR+HE0PyXCbifGIauG4D+x3p2WY9jT7OORRz4fFLR8RT8QLxT472bwEdJZSLBOCTButrp4ShCHqn0wTjG2+q0OQ6rT13DBVI6njwYqr2fA58sxefStGLgDxQslvzRrsUHZtD7vojxJuLPmxhgnv5lwyso1uLzkyX08bbUBR4m4C7GmGRXq5c7FDLZCrFl9z02jObvHaOI9LkBuMZsqs8sS9dnvjZ3rvgKzzDcIPER0hclOyEcugLVA23cLskqpH06vaQfZM3GEwJj8kQgYePzIAzvcIjj9EagpX9sMonxd1Qamc/5SCo65BeLZtJhQx5eBR6/DuEMhbe5rMVKEY7Sx+KIPMJVEDL0/YtiH0aQd3Oo6QZv170dDINFRPs8KkNEB2/9IKYgCzDrTPCGqfBWFbSQHIoJeil1ccG/QXiFSLyjU8Qo4CDYibaVtbcF5MSUZQlsog9ykC/ylQtF0wuu6556t2v0bock/fTP9HLBLj8gqpyYTvuIVuuQ6ZjPoY8rTau5cxvQT0T5ABLIwBR7iZg0i+Xb5MUk+FUhsl0GBD2tocdGt5SNqrCEscdVbpgqIJlheu2APBMIDgA3w5TgJjioBLe8r4w3ewi8eT6yfei7tgl1B8IrU6UVn1Iwb+KYLPECRzB8KHrLem2XPoXpDfMCbKU1irIeLne3Vd099Sb0E6I5wyh6CcgXhotri7e+cpSw6/uXcmPHG40apQwTe1wm2nL+58dvH9/Opt/evFt9fvjw9p/vVzr3pZRpF0hwQoM9M6G2giNG1I6D56rfq9N0eiuVKtyVJqxxEJGktPLfrKPA3NSoYs6RvJE0HWht0+lF9vyGZ+kSvnUJt5lg+V0tb2FKL21a0kKZ4JWFUhvIRXsYDU8VGi6oVoswSWMe2oEIZcALMw7DjI72msyoRGBaGwaCJDVa6O8u6h9I/aZjVdRvuK/pGK1Wx4jCGd7+HD4xopL4Ijwfa3wtPCRrFm4xbQm/KHypXvakuV89wpc2cW3Zjbb624F9qeJ38iRS+J2p1cuBHeB3DAhmnmFp0Jtrtu74Qha/Rjh2GBx0FQ7jR1V9aUJRmPASv4bG2sjiMG2eR/xrypGF1R4Ad8zviIBNp/7uEQJXFY/gODzmKZRK+wIbuoqNM0Rxi1sb0nkJ++/0XuZpnBewq9gDZr/saxDsmUrE/BceCI6VjF6vUFWBTpHtnxkcGyHXBtRecKyPk3o4UAHbseFoTSUqtKVYakwqbA/he2OvJgJucZMX7HVhb5wwft+QwJaR2RESdMyvhgQHwLdNnO27Ae+DJAmiBZ2HY1Gd6rkdMEfEW7IKlrZD/e+KUYYDKunHvpsFS7yabZLuesikvmhQg8aWOkJVU666naPX7efoQ2zo1KrRUNT4FSWKjOgnJlJxSK6yMLkEHgxv+MAq8P2UtqhKgh9wli7FLJijn65r3U6se7bWhuCEE8Uwcral+mzdvplZI2djLDGDPtbyyCA/ybfAYLKLvN7In8EktRYc0X9CmJD0lV7OYQ8N6b6FnDoXMrUdAO3J2KXChpLgkaZcLQmCaY0tj1USbAvjSxj7HAdpRRhvyHpDxlTxz1YNPkr18japccrNgFrVO2OkMnnwVZPKnGF96IhwUor3DosvzR7xpT1OJmNKuxNGv6pw90LT0apD9TGl28dNflr7+5wrybxpz8mtPndvExMnnmBmCLfeJn7O+ba45R3Ga975HyJkx1HOwiLJTgtr1J2qPV5rAHRgnSsPxQHVE4oZ1bPYnN7cwQHtlpUvOLxJuScwqaN4V8XrfRa75dnLJaray7W6r7jPWqvhI3e9fvea9SA0AFQaqM9lnVHcqymBEshZYl/3CqTsX1mowb3uWz+SC+0mjwca36t9/kj1I/Uc043PEthVXjMqijmXzGUgepU0fdJjTPU2qx5lymAQoW1RCqGBEPep5xWFKds7Q8df+qk4tu7EaL3CQCvCtGvNAKACsitBe0fysCOl3QC49GGHUbFr2dVAw6QvXv4DvZi5WFdMxPN5gkZJjgy14n6zXoepg6P5UBisglFLPL9DvGlar13EM4zjAs7zCh37E1Q374yzC6xLJY++hxKUhQy9I0ocKLhTXrjjwHzH/HGCO0Pd73hi3/oSXjyL7zIXf5Io79ckL/lAzatvQRhHfmfnki4PEMoZffPlhkhu4HTZBAdWo0/FqM6e6XLH/JEY9cgTQ2dmIwec4G/bijrRFk/vYGUcy9ItySDkw7t9LUteyOx59G1fy7KA9Bz3FJbiKLHHA8V2nJ4IkiOMvmd/9g8nBvDvhvRtD1BzXMitce/uaO7dHci9N+8zX9x7Jwk5PUnIPW/3DqRyjrLQQCRkSKcjgX6KBEitbtfsNrMj6L22msc9f9dgYMUPk7SwGf95Fb7+JD8KU/l9kWYuaeS+K+1a10XQcyhkxy/KiXOZIyY82rVz7t8i+GkocaxvtmgNifi5pjy6dHjxJCkMUAvYpz9r/CpcB7TJWXMdbRa/J5VNL360Czz8Dw==&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><ellipse cx="60" cy="89" rx="11" ry="11" fill="#000000" stroke="#ff0000" transform="rotate(90,60,89)" pointer-events="all"/><rect x="155" y="59" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 89px; margin-left: 156px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Render Template</div><div>with data srcs<br /></div></div></div></div></foreignObject><text x="215" y="93" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Render Template...</text></switch></g><path d="M 835 59 L 835 19 Q 835 9 825 9 L 225 9 Q 215 9 215 19 L 215 56.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 210.5 48.88 L 215 57.88 L 219.5 48.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="615" y="59" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 89px; margin-left: 616px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Template not completed</div></div></div></foreignObject><text x="675" y="93" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Template not complet...</text></switch></g><rect x="390" y="219" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 249px; margin-left: 391px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Template is ready</div></div></div></foreignObject><text x="450" y="253" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Template is ready</text></switch></g><path d="M 735 89 L 745 89 Q 755 89 763.88 89 L 772.76 89" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 764.88 93.5 L 773.88 89 L 764.88 84.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 75 89 L 152.76 89" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 144.88 93.5 L 153.88 89 L 144.88 84.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 275 89 L 305 89 Q 315 89 325 89 L 382.76 89" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 374.88 93.5 L 383.88 89 L 374.88 84.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 390 249 L 342.5 249 Q 332.5 249 332.5 239 L 332.5 199 Q 332.5 189 322.5 189 L 277.24 189" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 285.12 184.5 L 276.12 189 L 285.12 193.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 450 154 L 450 216.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 445.5 208.88 L 450 217.88 L 454.5 208.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 187px; margin-left: 452px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">No</div></div></div></foreignObject><text x="452" y="191" fill="#000000" font-family="Verdana" font-size="12px">No</text></switch></g><path d="M 515 89 L 595 89 Q 605 89 608.88 89 L 612.76 89" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 604.88 93.5 L 613.88 89 L 604.88 84.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 450 24 L 515 89 L 450 154 L 385 89 Z" fill="#ffffff" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 89px; margin-left: 386px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Missing or not</div><div>fetched yet</div><div>resources?</div></div></div></div></foreignObject><text x="450" y="93" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Missing or not...</text></switch></g><rect x="535" y="74" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 84px; margin-left: 555px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="555" y="88" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Yes</text></switch></g><rect x="390" y="334" width="120" height="60" rx="14.4" ry="14.4" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 364px; margin-left: 391px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Fetch data async</div><div>based on last idx<br /></div></div></div></div></foreignObject><text x="450" y="368" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Fetch data async...</text></switch></g><rect x="155" y="159" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 189px; margin-left: 156px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Write output</div></div></div></foreignObject><text x="215" y="193" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Write output</text></switch></g><path d="M 215 159 L 215 121.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 219.5 129.12 L 215 120.12 L 210.5 129.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 63.23 222.82 L 56.77 215.18 L 149.86 136.52 L 143.4 128.89 L 176 120.98 L 162.77 151.8 L 156.31 144.16 Z" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 169px; margin-left: 117px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><div>Update</div><div>data</div><div>async</div></div></div></div></foreignObject><text x="117" y="172" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Update...</text></switch></g><rect x="0" y="219" width="120" height="60" rx="14.4" ry="14.4" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 249px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Data fetched</div></div></div></foreignObject><text x="60" y="253" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Data fetched</text></switch></g><path d="M 390 364 L 285 364 Q 275 364 265 364 L 247.24 364" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 255.12 359.5 L 246.12 364 L 255.12 368.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="775" y="59" width="120" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 89px; margin-left: 776px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Add missing resources</div></div></div></foreignObject><text x="835" y="93" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Add missing resources</text></switch></g><path d="M 830 119 L 840 119 L 840 304 L 850 304 L 835 334 L 820 304 L 830 304 Z" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 229px; margin-left: 765px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Add new data to fetch</div></div></div></foreignObject><text x="765" y="232" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Add new data to fetch</text></switch></g><rect x="0" y="394" width="120" height="60" rx="14.4" ry="14.4" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 424px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Apply ratelimit</div></div></div></foreignObject><text x="60" y="428" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Apply ratelimit</text></switch></g><path d="M 60 279 L 60 299 Q 60 309 60 319 L 60 391.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 55.5 383.88 L 60 392.88 L 64.5 383.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="775" y="334" width="120" height="60" rx="14.4" ry="14.4" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 364px; margin-left: 776px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Start fetching new resources</div></div></div></foreignObject><text x="835" y="368" fill="#000000" font-family="Verdana" font-size="12px" text-anchor="middle">Start fetching new r...</text></switch></g><path d="M 775 364 L 685 364 Q 675 364 665 364 L 512.24 364" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 520.12 359.5 L 511.12 364 L 520.12 368.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 120 424 L 440 424 Q 450 424 450 414 L 450 396.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 454.5 404.12 L 450 395.12 L 445.5 404.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 205 324 L 245 364 L 205 404 L 165 364 Z" fill="#e1d5e7" stroke="#9673a6" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 364px; margin-left: 166px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Error?</div></div></div></foreignObject><text x="205" y="368" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Error?</text></switch></g><path d="M 205 324 L 205 259 Q 205 249 195 249 L 122.24 249" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 130.12 244.5 L 121.12 249 L 130.12 253.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 254px; margin-left: 206px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><div>No</div></div></div></div></foreignObject><text x="206" y="257" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">No</text></switch></g><path d="M 165 364 L 100 364 Q 90 364 90 374 L 90 391.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 85.5 383.88 L 90 392.88 L 94.5 383.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 365px; margin-left: 119px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Yes</div></div></div></foreignObject><text x="119" y="368" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Yes</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
@@ -9,7 +9,7 @@ module Consul
9
9
  class ConsulConfiguration
10
10
  attr_reader :base_url, :token, :retry_duration, :min_duration, :wait_duration, :max_retry_duration, :retry_on_non_diff,
11
11
  :missing_index_retry_time_on_diff, :missing_index_retry_time_on_unchanged, :debug, :enable_gzip_compression,
12
- :fail_fast_errors, :max_consecutive_errors_on_endpoint
12
+ :fail_fast_errors, :max_consecutive_errors_on_endpoint, :tls_cert_chain, :tls_private_key, :tls_verify_peer
13
13
  def initialize(base_url: 'http://localhost:8500',
14
14
  debug: { network: false },
15
15
  token: nil,
@@ -23,7 +23,10 @@ module Consul
23
23
  enable_gzip_compression: true,
24
24
  paths: {},
25
25
  max_consecutive_errors_on_endpoint: 10,
26
- fail_fast_errors: 1)
26
+ fail_fast_errors: 1,
27
+ tls_cert_chain: nil,
28
+ tls_private_key: nil,
29
+ tls_verify_peer: true)
27
30
  @base_url = base_url
28
31
  @token = token
29
32
  @debug = debug
@@ -38,6 +41,9 @@ module Consul
38
41
  @paths = paths
39
42
  @max_consecutive_errors_on_endpoint = max_consecutive_errors_on_endpoint
40
43
  @fail_fast_errors = fail_fast_errors
44
+ @tls_cert_chain = tls_cert_chain
45
+ @tls_private_key = tls_private_key
46
+ @tls_verify_peer = tls_verify_peer
41
47
  end
42
48
 
43
49
  def ch(path, symbol)
@@ -50,10 +56,15 @@ module Consul
50
56
  end
51
57
  end
52
58
 
53
- def create(path)
59
+ def create(path, agent: nil)
54
60
  return self unless @paths[path.to_sym]
55
61
 
56
- ConsulConfiguration.new(base_url: ch(path, :base_url),
62
+ base_url = ch(path, :base_url)
63
+ if agent
64
+ agent = "http://#{agent}" unless agent.start_with? 'http', 'https'
65
+ base_url = agent
66
+ end
67
+ ConsulConfiguration.new(base_url: base_url,
57
68
  debug: ch(path, :debug),
58
69
  token: ch(path, :token),
59
70
  retry_duration: ch(path, :retry_duration),
@@ -66,7 +77,10 @@ module Consul
66
77
  enable_gzip_compression: enable_gzip_compression,
67
78
  paths: @paths,
68
79
  max_consecutive_errors_on_endpoint: @max_consecutive_errors_on_endpoint,
69
- fail_fast_errors: @fail_fast_errors)
80
+ fail_fast_errors: @fail_fast_errors,
81
+ tls_cert_chain: ch(path, :tls_cert_chain),
82
+ tls_private_key: ch(path, :tls_private_key),
83
+ tls_verify_peer: ch(path, :tls_verify_peer))
70
84
  end
71
85
  end
72
86
 
@@ -128,8 +142,8 @@ module Consul
128
142
  # So, it basically performs all the optimizations to keep updated with Consul internal state.
129
143
  class ConsulEndpoint
130
144
  attr_reader :conf, :path, :x_consul_index, :queue, :stats, :last_result, :enforce_json_200, :start_time, :default_value, :query_params
131
- def initialize(conf, path, enforce_json_200 = true, query_params = {}, default_value = '[]')
132
- @conf = conf.create(path)
145
+ def initialize(conf, path, enforce_json_200 = true, query_params = {}, default_value = '[]', agent = nil)
146
+ @conf = conf.create(path, agent: agent)
133
147
  @default_value = default_value
134
148
  @path = path
135
149
  @queue = EM::Queue.new
@@ -228,6 +242,13 @@ module Consul
228
242
  connect_timeout: 5, # default connection setup timeout
229
243
  inactivity_timeout: conf.wait_duration + 1 + (conf.wait_duration / 16) # default connection inactivity (post-setup) timeout
230
244
  }
245
+ unless conf.tls_cert_chain.nil?
246
+ options[:tls] = {
247
+ cert_chain_file: conf.tls_cert_chain,
248
+ private_key_file: conf.tls_private_key,
249
+ verify_peer: conf.tls_verify_peer
250
+ }
251
+ end
231
252
  connection = {
232
253
  conn: EventMachine::HttpRequest.new(conf.base_url, options)
233
254
  }
@@ -48,19 +48,23 @@ module Consul
48
48
  end
49
49
 
50
50
  # Return the coordinates of datacenters
51
- def datacenters(dc: nil)
51
+ def datacenters(dc: nil, agent: nil)
52
52
  path = '/v1/coordinate/datacenters'
53
53
  query_params = {}
54
54
  query_params[:dc] = dc if dc
55
- @endp_manager.create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]')) }
55
+ @endp_manager.create_if_missing(path, query_params, agent: agent) do
56
+ ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]', agent))
57
+ end
56
58
  end
57
59
 
58
60
  # Returns the coordinates for all nodes of DC
59
- def nodes(dc: nil)
61
+ def nodes(dc: nil, agent: nil)
60
62
  path = '/v1/coordinate/nodes'
61
63
  query_params = {}
62
64
  query_params[:dc] = dc if dc
63
- @endp_manager.create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]')) }
65
+ @endp_manager.create_if_missing(path, query_params, agent: agent) do
66
+ ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]', agent))
67
+ end
64
68
  end
65
69
 
66
70
  # Computes the RTT between 2 nodes
@@ -124,7 +128,7 @@ module Consul
124
128
  end
125
129
 
126
130
  # https://www.consul.io/api/health.html#list-nodes-for-service
127
- def service(name, dc: nil, passing: false, tag: nil)
131
+ def service(name, dc: nil, passing: false, tag: nil, agent: nil)
128
132
  raise 'You must specify a name for a service' if name.nil?
129
133
 
130
134
  path = "/v1/health/service/#{name}"
@@ -132,70 +136,82 @@ module Consul
132
136
  query_params[:dc] = dc if dc
133
137
  query_params[:passing] = passing if passing
134
138
  query_params[:tag] = tag if tag
135
- create_if_missing(path, query_params) { ConsulTemplateService.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
139
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateService.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
136
140
  end
137
141
 
138
142
  # https://www.consul.io/api/health.html#list-checks-for-service
139
- def checks_for_service(name, dc: nil, passing: false)
143
+ def checks_for_service(name, dc: nil, passing: false, agent: nil)
140
144
  raise 'You must specify a name for a service' if name.nil?
141
145
 
142
146
  path = "/v1/health/checks/#{name}"
143
147
  query_params = {}
144
148
  query_params[:dc] = dc if dc
145
149
  query_params[:passing] = passing if passing
146
- create_if_missing(path, query_params) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
150
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
147
151
  end
148
152
 
149
153
  # https://www.consul.io/api/health.html#list-checks-for-node
150
- def checks_for_node(name, dc: nil, passing: false)
154
+ def checks_for_node(name, dc: nil, passing: false, agent: nil)
151
155
  raise 'You must specify a name for a service' if name.nil?
152
156
 
153
157
  path = "/v1/health/node/#{name}"
154
158
  query_params = {}
155
159
  query_params[:dc] = dc if dc
156
160
  query_params[:passing] = passing if passing
157
- create_if_missing(path, query_params) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
161
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
162
+ end
163
+
164
+ # https://www.consul.io/api-docs/health#list-checks-in-state
165
+ # Supported in Consul 1.7+
166
+ def checks_in_state(check_state, dc: nil, agent: nil)
167
+ valid_checks_states = %w[any critical passing warning]
168
+ raise "checks_in_state('#{check_state}'...) must be one of #{valid_checks_states}" unless valid_checks_states.include?(check_state)
169
+
170
+ path = "/v1/health/state/#{check_state}"
171
+ query_params = {}
172
+ query_params[:dc] = dc if dc
173
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
158
174
  end
159
175
 
160
176
  # https://www.consul.io/api/catalog.html#list-nodes
161
- def nodes(dc: nil)
177
+ def nodes(dc: nil, agent: nil)
162
178
  path = '/v1/catalog/nodes'
163
179
  query_params = {}
164
180
  query_params[:dc] = dc if dc
165
- create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
181
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
166
182
  end
167
183
 
168
184
  # https://www.consul.io/api/catalog.html#list-services-for-node
169
- def node(name_or_id, dc: nil)
185
+ def node(name_or_id, dc: nil, agent: nil)
170
186
  path = "/v1/catalog/node/#{name_or_id}"
171
187
  query_params = {}
172
188
  query_params[:dc] = dc if dc
173
- create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
189
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}', agent)) }
174
190
  end
175
191
 
176
192
  # https://www.consul.io/api/agent.html#read-configuration
177
- def agent_self
193
+ def agent_self(agent: nil)
178
194
  path = '/v1/agent/self'
179
195
  query_params = {}
180
196
  default_value = '{"Config":{}, "Coord":{}, "Member":{}, "Meta":{}, "Stats":{}}'
181
- create_if_missing(path, query_params) { ConsulAgentSelf.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
197
+ create_if_missing(path, query_params, agent: agent) { ConsulAgentSelf.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
182
198
  end
183
199
 
184
200
  # https://www.consul.io/api/agent.html#view-metrics
185
- def agent_metrics
201
+ def agent_metrics(agent: nil)
186
202
  path = '/v1/agent/metrics'
187
203
  query_params = {}
188
204
  default_value = '{"Gauges":[], "Points":[], "Member":{}, "Counters":[], "Samples":{}}'
189
- create_if_missing(path, query_params) { ConsulAgentMetrics.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
205
+ create_if_missing(path, query_params, agent: agent) { ConsulAgentMetrics.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
190
206
  end
191
207
 
192
208
  # https://www.consul.io/api/agent.html#list-members
193
- def agent_members(wan: false)
209
+ def agent_members(wan: false, agent: nil)
194
210
  path = '/v1/agent/members'
195
211
  query_params = {}
196
212
  query_params['wan'] = true if wan
197
213
  default_value = '[]'
198
- create_if_missing(path, query_params) { ConsulTemplateMembers.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
214
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateMembers.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
199
215
  end
200
216
 
201
217
  # Return a param of template
@@ -212,51 +228,57 @@ module Consul
212
228
  end
213
229
 
214
230
  # https://www.consul.io/api/catalog.html#list-services
215
- def services(dc: nil, tag: nil)
231
+ def services(dc: nil, tag: nil, agent: nil)
216
232
  path = '/v1/catalog/services'
217
233
  query_params = {}
218
234
  query_params[:dc] = dc if dc
219
235
  # Tag filtering is performed on client side
220
236
  query_params[:tag] = tag if tag
221
- create_if_missing(path, query_params) { ConsulTemplateServices.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
237
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateServices.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}', agent)) }
222
238
  end
223
239
 
224
240
  # https://www.consul.io/api/catalog.html#list-datacenters
225
- def datacenters
241
+ def datacenters(agent: nil)
226
242
  path = '/v1/catalog/datacenters'
227
243
  query_params = {}
228
- create_if_missing(path, query_params) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
244
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
229
245
  end
230
246
 
231
247
  # https://www.consul.io/api/kv.html#read-key
232
- def kv(name = nil, dc: nil, keys: nil, recurse: false)
248
+ def kv(name = nil, dc: nil, keys: nil, recurse: false, agent: nil)
233
249
  path = "/v1/kv/#{name}"
234
250
  query_params = {}
235
251
  query_params[:dc] = dc if dc
236
252
  query_params[:recurse] = recurse if recurse
237
253
  query_params[:keys] = keys if keys
238
254
  default_value = '[]'
239
- create_if_missing(path, query_params) { ConsulTemplateKV.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value), name) }
255
+ create_if_missing(path, query_params, agent: agent) { ConsulTemplateKV.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent), name) }
240
256
  end
241
257
 
242
- def secrets(path = '')
258
+ def secrets(path = '', agent: nil)
243
259
  raise "You need to provide a vault token to use 'secret' keyword" if vault_conf.token.nil?
244
260
 
245
261
  path = "/v1/#{path}".gsub(%r{/{2,}}, '/')
246
262
  query_params = { list: 'true' }
247
- create_if_missing(path, query_params, vault_conf.fail_fast_errors, vault_conf.max_consecutive_errors_on_endpoint) do
248
- ConsulTemplateVaultSecretList.new(VaultEndpoint.new(vault_conf, path, 'GET', true, query_params, JSON.generate(data: { keys: [] })))
263
+ create_if_missing(path, query_params,
264
+ fail_fast_errors: vault_conf.fail_fast_errors,
265
+ max_consecutive_errors_on_endpoint: vault_conf.max_consecutive_errors_on_endpoint,
266
+ agent: agent) do
267
+ ConsulTemplateVaultSecretList.new(VaultEndpoint.new(vault_conf, path, 'GET', true, query_params, JSON.generate(data: { keys: [] }), agent: agent))
249
268
  end
250
269
  end
251
270
 
252
- def secret(path = '', post_data = nil)
271
+ def secret(path = '', post_data = nil, agent: nil)
253
272
  raise "You need to provide a vault token to use 'secret' keyword" if vault_conf.token.nil?
254
273
 
255
274
  path = "/v1/#{path}".gsub(%r{/{2,}}, '/')
256
275
  query_params = {}
257
276
  method = post_data ? 'POST' : 'GET'
258
- create_if_missing(path, query_params, vault_conf.fail_fast_errors, vault_conf.max_consecutive_errors_on_endpoint) do
259
- ConsulTemplateVaultSecret.new(VaultEndpoint.new(vault_conf, path, method, true, query_params, JSON.generate(data: {})))
277
+ create_if_missing(path, query_params,
278
+ fail_fast_errors: vault_conf.fail_fast_errors,
279
+ max_consecutive_errors_on_endpoint: vault_conf.max_consecutive_errors_on_endpoint,
280
+ agent: agent) do
281
+ ConsulTemplateVaultSecret.new(VaultEndpoint.new(vault_conf, path, method, true, query_params, JSON.generate(data: {}), agent: agent))
260
282
  end
261
283
  end
262
284
 
@@ -372,10 +394,10 @@ module Consul
372
394
  VaultEndpoint.new(vault_conf, path, :POST, {}, {})
373
395
  end
374
396
 
375
- def create_if_missing(path, query_params, fail_fast_errors = @fail_fast_errors, max_consecutive_errors_on_endpoint = @max_consecutive_errors_on_endpoint)
397
+ def create_if_missing(path, query_params, fail_fast_errors: @fail_fast_errors, max_consecutive_errors_on_endpoint: @max_consecutive_errors_on_endpoint, agent: nil)
376
398
  fqdn = path.dup
377
399
  query_params.each_pair do |k, v|
378
- fqdn = "#{fqdn}&#{k}=#{v}"
400
+ fqdn = "#{agent}#{fqdn}&#{k}=#{v}"
379
401
  end
380
402
  tpl = @endpoints[fqdn]
381
403
  unless tpl