consul-templaterb 1.26.2 → 1.28.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -7
- data/.travis.yml +5 -5
- data/CHANGELOG.md +38 -0
- data/README.md +19 -4
- data/TemplateAPI.md +9 -0
- data/bin/consul-templaterb +52 -6
- data/lib/consul/async/consul_endpoint.rb +19 -3
- data/lib/consul/async/consul_template.rb +26 -8
- data/lib/consul/async/consul_template_render.rb +1 -1
- data/lib/consul/async/json_endpoint.rb +15 -2
- data/lib/consul/async/process_handler.rb +7 -1
- data/lib/consul/async/vault_endpoint.rb +16 -2
- data/lib/consul/async/version.rb +1 -1
- data/samples/checks_in_warning_or_critical_state.yaml.erb +13 -0
- data/samples/consul-ui/css/style.css +4 -0
- data/samples/consul-ui/decorators.js.erb +2 -171
- data/samples/consul-ui/js/nodes.js +1 -1
- data/samples/consul-ui/js/service.js +1 -1
- data/samples/consul-ui/js/utils.js +45 -20
- data/samples/display_timestamped_changes.txt.erb +17 -0
- data/samples/prometheus_datacenter_coordinates.erb +56 -0
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c15ee78520048d7d091882b9d3b1d43b4cc5f5658cf39d782f06e1dc617d8cd9
|
4
|
+
data.tar.gz: 6f77a9daab557420f2640a314a97f78af68e26fec91c1a66f3ee575d9bf150f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c2b374a9400061f3ffd673e418771bb5685ec3d13a631c3839660121d86575be1fbbd1cd99f7ee1a4ddedf850ec72fa984ca084575da587b7a239d2e4654b2e
|
7
|
+
data.tar.gz: '0933c0a2f1f58c617c184679ab68c1e90d55f1d9ab796e45336ddbbf49ae2fd73cf1aafbfea6e921254a520c8be3e5f0f380cfbdacd694ba2de6c5f8e4f5a074'
|
data/.rubocop.yml
CHANGED
@@ -7,28 +7,28 @@ Layout/LineLength:
|
|
7
7
|
Max: 175
|
8
8
|
|
9
9
|
Metrics/AbcSize:
|
10
|
-
Max:
|
10
|
+
Max: 87
|
11
11
|
|
12
12
|
Metrics/BlockLength:
|
13
|
-
Max:
|
13
|
+
Max: 188
|
14
14
|
|
15
15
|
Metrics/BlockNesting:
|
16
16
|
Max: 4
|
17
17
|
|
18
18
|
Metrics/ClassLength:
|
19
|
-
Max:
|
19
|
+
Max: 285
|
20
20
|
|
21
21
|
Metrics/CyclomaticComplexity:
|
22
|
-
Max:
|
22
|
+
Max: 21
|
23
23
|
|
24
24
|
Metrics/MethodLength:
|
25
|
-
Max:
|
25
|
+
Max: 68
|
26
26
|
|
27
27
|
Metrics/ParameterLists:
|
28
|
-
Max:
|
28
|
+
Max: 18
|
29
29
|
|
30
30
|
Metrics/PerceivedComplexity:
|
31
|
-
Max:
|
31
|
+
Max: 24
|
32
32
|
|
33
33
|
# We use `dc` as a parameter in many methods
|
34
34
|
Naming/MethodParameterName:
|
data/.travis.yml
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.4.
|
4
|
-
- 2.5.
|
5
|
-
- 2.6.
|
6
|
-
- 2.7.
|
3
|
+
- 2.4.10
|
4
|
+
- 2.5.8
|
5
|
+
- 2.6.6
|
6
|
+
- 2.7.1
|
7
7
|
jobs:
|
8
8
|
include:
|
9
9
|
- stage: Gem release
|
10
|
-
rvm: 2.5.
|
10
|
+
rvm: 2.5.8
|
11
11
|
script: echo "Publishing consul-templaterb on rubygems.org ..."
|
12
12
|
deploy:
|
13
13
|
provider: rubygems
|
data/CHANGELOG.md
CHANGED
@@ -2,8 +2,46 @@
|
|
2
2
|
|
3
3
|
## (UNRELEASED)
|
4
4
|
|
5
|
+
## 1.28.0 (Sept 25, 2020)
|
6
|
+
|
7
|
+
NEW FEATURES:
|
8
|
+
|
9
|
+
* Added `-W` or `--wait-between-reload-signal` to avoid sending too many signals
|
10
|
+
to a process executed. This avoids for instance reloading too much a HAProxy configuration
|
11
|
+
without having to play with `-w` as described in [#69](https://github.com/criteo/consul-templaterb/issues/69)
|
12
|
+
|
13
|
+
BUGFIX:
|
14
|
+
|
15
|
+
* Removed warnings at runtime with Ruby 2.7+
|
16
|
+
* Minor JS fix in Consul-UI (Added missing unused parameter to function `serviceTitleGenerator`)
|
17
|
+
|
18
|
+
## 1.27.2 (Sept 4, 2020)
|
19
|
+
|
20
|
+
IMPROVEMENTS:
|
21
|
+
|
22
|
+
* Consul-UI now supports navigation between nodes and services in both ways
|
23
|
+
|
24
|
+
## 1.27.1 (July 28, 2020)
|
25
|
+
|
26
|
+
BUGIX:
|
27
|
+
|
28
|
+
* Fix collision in JSON queries when using payload in requests #68
|
29
|
+
|
30
|
+
## 1.27.0 (June 5, 2020)
|
31
|
+
|
5
32
|
NEW FEATURES:
|
6
33
|
|
34
|
+
* For Consul 1.7+, now support `checks_in_state(check_state, dc: nil, [agent: consul_agent_address])`,
|
35
|
+
fixes feature [#65](https://github.com/criteo/consul-templaterb/issues/65)
|
36
|
+
* New options to support/disable TLS validation thanks to [@jeromegn](https://github.com/jeromegn)
|
37
|
+
[#66](https://github.com/criteo/consul-templaterb/pull/66)
|
38
|
+
|
39
|
+
## 1.26.3 (April 15, 2020)
|
40
|
+
|
41
|
+
BUGFIX:
|
42
|
+
|
43
|
+
* Removed all Criteo specific decorators from Consul-UI
|
44
|
+
|
7
45
|
## 1.26.2 (April 15, 2020)
|
8
46
|
|
9
47
|
BUGFIX:
|
data/README.md
CHANGED
@@ -165,9 +165,19 @@ USAGE: consul-templaterb [[options]]
|
|
165
165
|
-f, --[no-]fail-fast If consul/vault endpoints fail at startup, fail immediately
|
166
166
|
-g, --no-gzip-compression Disable GZIP compression in HTTP requests
|
167
167
|
-c, --consul-addr=<address> Address of Consul, eg: http://localhost:8500
|
168
|
+
--consul-cert-chain=<path/to/cert_chain>
|
169
|
+
Path to Consul TLS client certificate chain to use
|
170
|
+
--consul-private-key=<path/to/private_key>
|
171
|
+
Path to Consul TLS client private key to use
|
172
|
+
--skip-consul-verify-tls Skip verifying Consul TLS via certificate authority (DANGEROUS)
|
168
173
|
-l, --log-level=<log_level> Log level, default=info, any of none|error|info|debug
|
169
174
|
--consul-token=<token> Use a token to connect to Consul
|
170
175
|
-V, --vault-addr=<address> Address of Vault, eg: http://localhost:8200
|
176
|
+
--vault-cert-chain=<path/to/cert_chain>
|
177
|
+
Path to Vault TLS client certificate chain to use
|
178
|
+
--vault-private-key=<path/to/private_key>
|
179
|
+
Path to Vault TLS client private key to use
|
180
|
+
--skip-vault-verify-tls Skip verifying Vault TLS via certificate authority (DANGEROUS)
|
171
181
|
--vault-token=<token> Token used to authenticate against vault.
|
172
182
|
--[no-]vault-renew Control auto-renewal of the Vault token. Default: activated
|
173
183
|
--vault-retry, --vault-retry-attempts [RETRIES]
|
@@ -178,10 +188,11 @@ USAGE: consul-templaterb [[options]]
|
|
178
188
|
-r, --retry-delay=<min_duration> Min Retry delay on Error/Missing Consul Index
|
179
189
|
-k, --hot-reload=<behavior> Control hot reload behaviour, one of :[die (kill daemon on hot reload failure), keep (on error, keep running), disable (hot reload disabled)]
|
180
190
|
-K, --sig-term=kill_signal Signal to send to next --exec command on kill, default=TERM
|
191
|
+
-M, --debug-memory-usage Display messages when RAM grows
|
181
192
|
-T, --trim-mode=trim_mode ERB Trim mode to use (- by default)
|
182
193
|
-R, --sig-reload=reload_signal Signal to send to next --exec command on reload (NONE supported), default=HUP
|
183
|
-
-
|
184
|
-
-e, --exec=<command> Execute the following command
|
194
|
+
-W, --wait-signal=min_duration Wait at least n seconds before each reload signal being sent to next --exec process
|
195
|
+
-e, --exec=<command> Execute the following command in as a subprocess when all templates are ready
|
185
196
|
-d, --debug-network-usage Debug the network usage
|
186
197
|
-t erb_file:[output]:[command]:[params_file],
|
187
198
|
--template Add a erb template, its output and optional reload command
|
@@ -235,7 +246,11 @@ nor write the file.
|
|
235
246
|
Signals can be customized per process. Two signals are supported with options `--sig-reload` and
|
236
247
|
`--sig-term`. When the option is added, the next `--exec` options to start a process will use the
|
237
248
|
given signal. By default, HUP will be sent to reload events (you can use NONE to avoid sending any
|
238
|
-
reload signal), TERM will be used when leaving consul-templaterb.
|
249
|
+
reload signal), TERM will be used when leaving consul-templaterb. A minimum duration between reload
|
250
|
+
signals can be specified for each sub process by prepending `--wait-signal=min_duration` to `--exec`
|
251
|
+
command.
|
252
|
+
In such case, the signal will be sent every `min_duration` as a maximum (very useful for templates
|
253
|
+
changing a lot, but you don't want to trigger too many reloads, for instance for a load-balancer).
|
239
254
|
|
240
255
|
### Bandwidth limitation
|
241
256
|
|
@@ -332,7 +347,7 @@ Please consult [CHANGELOG.md](CHANGELOG.md) for fixed bugs.
|
|
332
347
|
|
333
348
|
## TODO
|
334
349
|
|
335
|
-
* [x] Hashi's Vault support
|
350
|
+
* [x] Hashi's Vault support
|
336
351
|
* [ ] Implement automatic dynamic rate limit
|
337
352
|
* [x] More samples: apache, nginx, a full website displaying consul information...
|
338
353
|
* [x] Optimize rendering speed at start-up: an iteration is done every second by default, but it would be possible to speed
|
data/TemplateAPI.md
CHANGED
@@ -388,6 +388,15 @@ name or its ID. If DC is specified, will lookup for given node in another datace
|
|
388
388
|
|
389
389
|
[Find all the checks](https://www.consul.io/api/health.html#list-checks-for-service) of a given service.
|
390
390
|
|
391
|
+
## def checks_in_state(check_state, dc: nil, [agent: consul_agent_address])
|
392
|
+
|
393
|
+
[Find all the checks in a given state](https://www.consul.io/api-docs/health#list-checks-in-state) in the whole cluster.
|
394
|
+
|
395
|
+
The filter check_state must be one of any|critical|warning|passing.
|
396
|
+
|
397
|
+
Warning: this endpoint might be very frequently updated in a
|
398
|
+
large cluster if you are using `any` value. This endpoint is supported with Consul 1.7+.
|
399
|
+
|
391
400
|
## kv(name, [dc: nil], [keys: false], [recurse: false], [agent: consul_agent_address])
|
392
401
|
|
393
402
|
[Read keys from KV Store](https://www.consul.io/api/kv.html#read-key). It can be used for both listing the keys and
|
data/bin/consul-templaterb
CHANGED
@@ -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
|
@@ -88,6 +94,7 @@ consul_engine = Consul::Async::ConsulTemplateEngine.new
|
|
88
94
|
@programs = {}
|
89
95
|
cur_sig_reload = 'HUP'.freeze
|
90
96
|
cur_sig_term = 'TERM'.freeze
|
97
|
+
cur_min_duration_between_signals = 1
|
91
98
|
|
92
99
|
optparse = OptionParser.new do |opts|
|
93
100
|
opts.banner = usage_text
|
@@ -122,6 +129,18 @@ optparse = OptionParser.new do |opts|
|
|
122
129
|
options[:consul][:base_url] = consul_url
|
123
130
|
end
|
124
131
|
|
132
|
+
opts.on('--consul-cert-chain=<path/to/cert_chain>', String, 'Path to Consul TLS client certificate chain to use') do |consul_client_cert|
|
133
|
+
options[:consul][:tls_cert_chain] = consul_client_cert
|
134
|
+
end
|
135
|
+
|
136
|
+
opts.on('--consul-private-key=<path/to/private_key>', String, 'Path to Consul TLS client private key to use') do |consul_client_key|
|
137
|
+
options[:consul][:tls_private_key] = consul_client_key
|
138
|
+
end
|
139
|
+
|
140
|
+
opts.on('--skip-consul-verify-tls', 'Skip verifying Consul TLS via certificate authority (DANGEROUS)') do
|
141
|
+
options[:consul][:tls_verify_peer] = false
|
142
|
+
end
|
143
|
+
|
125
144
|
opts.on('-l', '--log-level=<log_level>', String, "Log level, default=info, any of #{::Consul::Async::Debug.levels.join('|')}") do |log_level|
|
126
145
|
::Consul::Async::Debug.level = log_level
|
127
146
|
end
|
@@ -134,6 +153,18 @@ optparse = OptionParser.new do |opts|
|
|
134
153
|
options[:vault][:base_url] = vault_url
|
135
154
|
end
|
136
155
|
|
156
|
+
opts.on('--vault-cert-chain=<path/to/cert_chain>', String, 'Path to Vault TLS client certificate chain to use') do |vault_client_cert|
|
157
|
+
options[:vault][:tls_cert_chain] = vault_client_cert
|
158
|
+
end
|
159
|
+
|
160
|
+
opts.on('--vault-private-key=<path/to/private_key>', String, 'Path to Vault TLS client private key to use') do |vault_client_key|
|
161
|
+
options[:vault][:tls_private_key] = vault_client_key
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.on('--skip-vault-verify-tls', 'Skip verifying Vault TLS via certificate authority (DANGEROUS)') do
|
165
|
+
options[:vault][:tls_verify_peer] = false
|
166
|
+
end
|
167
|
+
|
137
168
|
opts.on('-T', '--vault-token=<token>', String, 'Token used to authenticate against vault.') do |vault_token|
|
138
169
|
options[:vault][:token] = vault_token
|
139
170
|
end
|
@@ -185,6 +216,10 @@ optparse = OptionParser.new do |opts|
|
|
185
216
|
cur_sig_term = compute_signal(sig, nil)
|
186
217
|
end
|
187
218
|
|
219
|
+
opts.on('-M', '--debug-memory-usage', 'Display messages when RAM grows') do
|
220
|
+
consul_engine.debug_memory = true
|
221
|
+
end
|
222
|
+
|
188
223
|
opts.on('-T', '--trim-mode=trim_mode', String,
|
189
224
|
"ERB Trim mode to use (#{options[:erb][:trim_mode]} by default)") do |trim_mode|
|
190
225
|
options[:erb][:trim_mode] = trim_mode
|
@@ -195,25 +230,36 @@ optparse = OptionParser.new do |opts|
|
|
195
230
|
cur_sig_reload = compute_signal(sig, 'NONE')
|
196
231
|
end
|
197
232
|
|
198
|
-
opts.on('-
|
199
|
-
|
233
|
+
opts.on('-W', '--wait-signal=min_duration', Float, 'Wait at least n seconds before each reload signal being sent to next --exec process') do |min_duration|
|
234
|
+
raise "-wait-between-reload-signal=#{min_duration} must be greater than 0" unless min_duration.positive?
|
235
|
+
|
236
|
+
cur_min_duration_between_signals = min_duration
|
200
237
|
end
|
201
238
|
|
202
|
-
opts.on('-e', '--exec=<command>', String, 'Execute the following command') do |cmd|
|
239
|
+
opts.on('-e', '--exec=<command>', String, 'Execute the following command in as a subprocess when all templates are ready') do |cmd|
|
203
240
|
sig_reload = cur_sig_reload
|
204
241
|
sig_term = cur_sig_term
|
242
|
+
sig_min_interval = cur_min_duration_between_signals
|
205
243
|
consul_engine.add_template_callback do |all_ready, template_manager, results|
|
206
244
|
if all_ready
|
207
245
|
modified = results.any?(&:modified)
|
208
246
|
if @programs[cmd].nil?
|
209
|
-
warn "[EXEC] Starting process: #{cmd}... on_reload=#{sig_reload || 'NONE'} on_term=#{sig_term}"
|
247
|
+
warn "[EXEC] Starting process: #{cmd}... on_reload=#{sig_reload || 'NONE'} on_term=#{sig_term}, delay between reloads=#{sig_min_interval}s"
|
210
248
|
@programs[cmd] = Consul::Async::ProcessHandler.new(cmd, sig_reload: sig_reload, sig_term: sig_term)
|
211
249
|
@programs[cmd].start
|
212
250
|
else
|
251
|
+
|
213
252
|
# At least one template has been modified
|
214
|
-
@programs[cmd]
|
253
|
+
process_to_reload = @programs[cmd]
|
254
|
+
if modified && !process_to_reload.reload_scheduled
|
255
|
+
process_to_reload.reload_scheduled = true
|
256
|
+
now = Time.now
|
257
|
+
delay = sig_min_interval - (now - @programs[cmd].last_signal_sent)
|
258
|
+
delay = 0 if delay.negative?
|
259
|
+
EventMachine.add_timer(delay) { process_to_reload.reload }
|
260
|
+
end
|
215
261
|
begin
|
216
|
-
|
262
|
+
process_to_reload.process_status
|
217
263
|
rescue Consul::Async::ProcessDoesNotExist => e
|
218
264
|
warn "[FATAL] The process is dead, aborting run: #{e.inspect}"
|
219
265
|
template_manager.terminate
|
@@ -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)
|
@@ -71,7 +77,10 @@ module Consul
|
|
71
77
|
enable_gzip_compression: enable_gzip_compression,
|
72
78
|
paths: @paths,
|
73
79
|
max_consecutive_errors_on_endpoint: @max_consecutive_errors_on_endpoint,
|
74
|
-
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))
|
75
84
|
end
|
76
85
|
end
|
77
86
|
|
@@ -233,6 +242,13 @@ module Consul
|
|
233
242
|
connect_timeout: 5, # default connection setup timeout
|
234
243
|
inactivity_timeout: conf.wait_duration + 1 + (conf.wait_duration / 16) # default connection inactivity (post-setup) timeout
|
235
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
|
236
252
|
connection = {
|
237
253
|
conn: EventMachine::HttpRequest.new(conf.base_url, options)
|
238
254
|
}
|
@@ -31,7 +31,8 @@ module Consul
|
|
31
31
|
|
32
32
|
def as_json(url, default_value, refresh_delay_secs: 10, **opts)
|
33
33
|
conf = JSONConfiguration.new(url: url, min_duration: refresh_delay_secs, retry_on_non_diff: refresh_delay_secs, **opts)
|
34
|
-
|
34
|
+
endpoint_id = url + opts.to_json
|
35
|
+
@endp_manager.create_if_missing(url, {}, endpoint_id: endpoint_id) do
|
35
36
|
if default_value.is_a?(Array)
|
36
37
|
ConsulTemplateJSONArray.new(JSONEndpoint.new(conf, url, default_value))
|
37
38
|
else
|
@@ -161,6 +162,18 @@ module Consul
|
|
161
162
|
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
162
163
|
end
|
163
164
|
|
165
|
+
# https://www.consul.io/api-docs/health#list-checks-in-state
|
166
|
+
# Supported in Consul 1.7+
|
167
|
+
def checks_in_state(check_state, dc: nil, agent: nil)
|
168
|
+
valid_checks_states = %w[any critical passing warning]
|
169
|
+
raise "checks_in_state('#{check_state}'...) must be one of #{valid_checks_states}" unless valid_checks_states.include?(check_state)
|
170
|
+
|
171
|
+
path = "/v1/health/state/#{check_state}"
|
172
|
+
query_params = {}
|
173
|
+
query_params[:dc] = dc if dc
|
174
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
175
|
+
end
|
176
|
+
|
164
177
|
# https://www.consul.io/api/catalog.html#list-nodes
|
165
178
|
def nodes(dc: nil, agent: nil)
|
166
179
|
path = '/v1/catalog/nodes'
|
@@ -382,16 +395,21 @@ module Consul
|
|
382
395
|
VaultEndpoint.new(vault_conf, path, :POST, {}, {})
|
383
396
|
end
|
384
397
|
|
385
|
-
def create_if_missing(path, query_params, fail_fast_errors: @fail_fast_errors,
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
398
|
+
def create_if_missing(path, query_params, fail_fast_errors: @fail_fast_errors,
|
399
|
+
max_consecutive_errors_on_endpoint: @max_consecutive_errors_on_endpoint,
|
400
|
+
agent: nil, endpoint_id: nil)
|
401
|
+
endpoint_id ||= begin
|
402
|
+
fqdn = path.dup
|
403
|
+
query_params.each_pair do |k, v|
|
404
|
+
fqdn = "#{agent}#{fqdn}&#{k}=#{v}"
|
405
|
+
end
|
406
|
+
fqdn
|
407
|
+
end
|
408
|
+
tpl = @endpoints[endpoint_id]
|
391
409
|
unless tpl
|
392
410
|
tpl = yield
|
393
411
|
::Consul::Async::Debug.print_debug "path #{path.ljust(64)} #{query_params.inspect}\r"
|
394
|
-
@endpoints[
|
412
|
+
@endpoints[endpoint_id] = tpl
|
395
413
|
tpl.endpoint.on_response do |result|
|
396
414
|
@net_info[:success] += 1
|
397
415
|
@net_info[:bytes_read] += result.data.bytesize
|
@@ -59,7 +59,7 @@ module Consul
|
|
59
59
|
return false unless new_template != @template
|
60
60
|
|
61
61
|
# We render to ensure the template is valid
|
62
|
-
render(new_template, current_template_info)
|
62
|
+
render(new_template, current_template_info: current_template_info)
|
63
63
|
@template = new_template.freeze
|
64
64
|
true
|
65
65
|
end
|
@@ -9,7 +9,7 @@ module Consul
|
|
9
9
|
class JSONConfiguration
|
10
10
|
attr_reader :url, :retry_duration, :min_duration, :retry_on_non_diff,
|
11
11
|
:debug, :enable_gzip_compression, :request_method, :json_body,
|
12
|
-
:headers
|
12
|
+
:headers, :tls_cert_chain, :tls_private_key, :tls_verify_peer
|
13
13
|
def initialize(url:,
|
14
14
|
debug: { network: false },
|
15
15
|
retry_duration: 10,
|
@@ -18,7 +18,10 @@ module Consul
|
|
18
18
|
request_method: :get,
|
19
19
|
json_body: nil,
|
20
20
|
headers: {},
|
21
|
-
enable_gzip_compression: true
|
21
|
+
enable_gzip_compression: true,
|
22
|
+
tls_cert_chain: nil,
|
23
|
+
tls_private_key: nil,
|
24
|
+
tls_verify_peer: true)
|
22
25
|
@url = url
|
23
26
|
@debug = debug
|
24
27
|
@enable_gzip_compression = enable_gzip_compression
|
@@ -28,6 +31,9 @@ module Consul
|
|
28
31
|
@request_method = request_method
|
29
32
|
@json_body = json_body
|
30
33
|
@headers = headers
|
34
|
+
@tls_cert_chain = tls_cert_chain
|
35
|
+
@tls_private_key = tls_private_key
|
36
|
+
@tls_verify_peer = tls_verify_peer
|
31
37
|
end
|
32
38
|
|
33
39
|
def create(_url)
|
@@ -181,6 +187,13 @@ module Consul
|
|
181
187
|
connect_timeout: 5, # default connection setup timeout
|
182
188
|
inactivity_timeout: 60 # default connection inactivity (post-setup) timeout
|
183
189
|
}
|
190
|
+
unless conf.tls_cert_chain.nil?
|
191
|
+
options[:tls] = {
|
192
|
+
cert_chain_file: conf.tls_cert_chain,
|
193
|
+
private_key_file: conf.tls_private_key,
|
194
|
+
verify_peer: conf.tls_verify_peer
|
195
|
+
}
|
196
|
+
end
|
184
197
|
connection = {
|
185
198
|
conn: EventMachine::HttpRequest.new(conf.url, options)
|
186
199
|
}
|
@@ -7,7 +7,8 @@ module Consul
|
|
7
7
|
# Handle the full lifecycle of a process and allows to forward
|
8
8
|
# Posix signals to child process when needed.
|
9
9
|
class ProcessHandler
|
10
|
-
attr_reader :command, :sig_reload, :sig_term, :pid, :exit_status
|
10
|
+
attr_reader :command, :sig_reload, :sig_term, :pid, :exit_status, :last_signal_sent, :reload_scheduled
|
11
|
+
attr_writer :reload_scheduled
|
11
12
|
def initialize(command, sig_reload: 'HUP', sig_term: 'TERM')
|
12
13
|
raise 'empty sig_term is not supported' unless sig_term
|
13
14
|
|
@@ -16,18 +17,23 @@ module Consul
|
|
16
17
|
@sig_term = sig_term
|
17
18
|
@pid = nil
|
18
19
|
@exit_status = nil
|
20
|
+
@last_signal_sent = Time.now
|
21
|
+
@reload_scheduled = false
|
19
22
|
end
|
20
23
|
|
21
24
|
def start
|
22
25
|
return pid unless pid.nil?
|
23
26
|
|
24
27
|
@pid = Process.spawn(command)
|
28
|
+
@last_signal_sent = Time.now
|
25
29
|
end
|
26
30
|
|
27
31
|
def reload
|
28
32
|
return if sig_reload.nil?
|
29
33
|
|
34
|
+
@last_signal_sent = Time.now
|
30
35
|
warn "Sending SIG #{sig_reload} to #{pid}..."
|
36
|
+
@reload_scheduled = false
|
31
37
|
begin
|
32
38
|
Process.kill(sig_reload, pid)
|
33
39
|
rescue Errno::ESRCH => e
|
@@ -10,7 +10,8 @@ module Consul
|
|
10
10
|
# Configuration for Vault Endpoints
|
11
11
|
class VaultConfiguration
|
12
12
|
attr_reader :base_url, :token, :token_renew, :retry_duration, :min_duration, :wait_duration, :max_retry_duration, :retry_on_non_diff,
|
13
|
-
:lease_duration_factor, :debug, :max_consecutive_errors_on_endpoint, :fail_fast_errors
|
13
|
+
:lease_duration_factor, :debug, :max_consecutive_errors_on_endpoint, :fail_fast_errors, :tls_cert_chain, :tls_private_key,
|
14
|
+
:tls_verify_peer
|
14
15
|
|
15
16
|
def initialize(base_url: 'http://localhost:8200',
|
16
17
|
debug: { network: false },
|
@@ -22,7 +23,10 @@ module Consul
|
|
22
23
|
max_retry_duration: 600,
|
23
24
|
paths: {},
|
24
25
|
max_consecutive_errors_on_endpoint: 10,
|
25
|
-
fail_fast_errors: false
|
26
|
+
fail_fast_errors: false,
|
27
|
+
tls_cert_chain: nil,
|
28
|
+
tls_private_key: nil,
|
29
|
+
tls_verify_peer: true)
|
26
30
|
@base_url = base_url
|
27
31
|
@token_renew = token_renew
|
28
32
|
@debug = debug
|
@@ -34,6 +38,9 @@ module Consul
|
|
34
38
|
@token = token
|
35
39
|
@max_consecutive_errors_on_endpoint = max_consecutive_errors_on_endpoint
|
36
40
|
@fail_fast_errors = fail_fast_errors
|
41
|
+
@tls_cert_chain = tls_cert_chain
|
42
|
+
@tls_private_key = tls_private_key
|
43
|
+
@tls_verify_peer = tls_verify_peer
|
37
44
|
end
|
38
45
|
|
39
46
|
def ch(path, symbol)
|
@@ -226,6 +233,13 @@ module Consul
|
|
226
233
|
connect_timeout: 5, # default connection setup timeout
|
227
234
|
inactivity_timeout: 1 # default connection inactivity (post-setup) timeout
|
228
235
|
}
|
236
|
+
unless conf.tls_cert_chain.nil?
|
237
|
+
options[:tls] = {
|
238
|
+
cert_chain_file: conf.tls_cert_chain,
|
239
|
+
private_key_file: conf.tls_private_key,
|
240
|
+
verify_peer: conf.tls_verify_peer
|
241
|
+
}
|
242
|
+
end
|
229
243
|
connection = EventMachine::HttpRequest.new(conf.base_url, options)
|
230
244
|
cb = proc do |_|
|
231
245
|
http = connection.send(http_method.downcase, build_request) # Under the hood: c.send('get', {stuff}) === c.get({stuff})
|
data/lib/consul/async/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
<%=
|
2
|
+
# This sample displays checks for the whole cluster
|
3
|
+
# in warning or critical state
|
4
|
+
# API available with Consul 1.7+
|
5
|
+
res = []
|
6
|
+
checks_in_state('warning').each do |c|
|
7
|
+
res << c
|
8
|
+
end
|
9
|
+
checks_in_state('critical').each do |c|
|
10
|
+
res << c
|
11
|
+
end
|
12
|
+
YAML.dump({'warning_or_critical_checks' => res})
|
13
|
+
%>
|
@@ -1,18 +1,5 @@
|
|
1
|
-
//
|
1
|
+
// Use this file to configure custom decorators for your Consul-UI
|
2
2
|
var httpRegexp = new RegExp('^http[s]?://[^ ]+$');
|
3
|
-
var dc = "<%= ENV['CRITEO_DC'] || 'par'%>";
|
4
|
-
var env = "<%= ENV['CRITEO_ENV'] || 'preprod'%>";
|
5
|
-
|
6
|
-
var availability_url = 'https://grafana.crto.in/d/xFX5gCnWz/service-availability?var-datacenter=' + dc + '&var-service=';
|
7
|
-
if (env == 'preprod') {
|
8
|
-
availability_url = 'https://grafana.preprod.crto.in/d/E0ANGjnZz/service-availability?var-datacenter=' + dc + '&var-service=';
|
9
|
-
}
|
10
|
-
var swagger_url = 'https://swaggercatalogapp.' + dc + '.' + env + '.crto.in/explore/swagger?key=';
|
11
|
-
var slack_url = 'https://criteo.slack.com/app_redirect?channel=';
|
12
|
-
|
13
|
-
var rackguru_url = 'https://rackguru.' + env + '.crto.in/serial/';
|
14
|
-
|
15
|
-
var asapi_url = 'https://idm.' + env + '.crto.in/tool/multiGroupInfo/'
|
16
3
|
|
17
4
|
function url_decorator(key, value) {
|
18
5
|
var e = document.createElement('a');
|
@@ -26,27 +13,7 @@ function usefullLinksGenerator(instance, serviceName, node_meta_info) {
|
|
26
13
|
top.className = 'instance-links';
|
27
14
|
|
28
15
|
var usefullLinks = [
|
29
|
-
{
|
30
|
-
title: "Trigger security scan",
|
31
|
-
iconClassName: "fas fa-shield-alt",
|
32
|
-
href: "https://security.crto.in/#/scan/?ip=" + instance.addr
|
33
|
-
},
|
34
|
-
{
|
35
|
-
title: "Availability Graph",
|
36
|
-
iconClassName: "fas fa-chart-area",
|
37
|
-
href: availability_url + serviceName
|
38
|
-
},
|
39
16
|
];
|
40
|
-
if (node_meta_info!= null) {
|
41
|
-
var serial = node_meta_info['serial_number'];
|
42
|
-
if (serial != null) {
|
43
|
-
usefullLinks.push({
|
44
|
-
title: "RackGuru",
|
45
|
-
iconClassName: "fas fa-server",
|
46
|
-
href: rackguru_url + serial
|
47
|
-
});
|
48
|
-
}
|
49
|
-
}
|
50
17
|
var first = true;
|
51
18
|
for (let usefullLink of usefullLinks) {
|
52
19
|
link = document.createElement('a');
|
@@ -134,19 +101,6 @@ function groups_decorator(instance, key, value, serviceName) {
|
|
134
101
|
return span;
|
135
102
|
}
|
136
103
|
|
137
|
-
/**
|
138
|
-
* Decorates with a slack channel link
|
139
|
-
*/
|
140
|
-
function slack_channel(instance, key, value, serviceName) {
|
141
|
-
var sName = value;
|
142
|
-
if (sName.startsWith('#')) {
|
143
|
-
sName = sName.substring(1);
|
144
|
-
}
|
145
|
-
return build_link(slack_url + encodeURIComponent(sName), '#'+ sName);
|
146
|
-
}
|
147
|
-
|
148
|
-
const start_regexp = /(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z/
|
149
|
-
|
150
104
|
function decorateIsoDate(value, formated) {
|
151
105
|
const parsed = Date.parse(formated);
|
152
106
|
if (isNaN(parsed)) {
|
@@ -163,55 +117,6 @@ function decorateIsoDate(value, formated) {
|
|
163
117
|
// This is the list of function called
|
164
118
|
// When a service meta is found
|
165
119
|
var registered_decorators = {
|
166
|
-
alert_availability_slack_channel: slack_channel,
|
167
|
-
gerrit_repository: function(instance, key, value, serviceName) {
|
168
|
-
return build_link('https://review.crto.in/#/q/' + value, value);
|
169
|
-
},
|
170
|
-
version: function(instance, key, value, serviceName) {
|
171
|
-
var asInt = parseInt(value);
|
172
|
-
if (asInt < 10000) {
|
173
|
-
return document.createTextNode(value);
|
174
|
-
}
|
175
|
-
if (instance.sMeta['CRITEO_APP_POOL'] != null) {
|
176
|
-
return build_link("https://devtools.crto.in/log.html?moab=cs&im=nb-to&range=100%2C" + asInt, value);
|
177
|
-
} else {
|
178
|
-
var tUrl = "https://devtools.crto.in/log.html?moab=j&im=nb-to&range=100%2C" + asInt;
|
179
|
-
if (instance.sMeta['jvm_artifact'] != null) {
|
180
|
-
tUrl += '&with-dependencies=true&artifacts=' + encodeURIComponent(instance.sMeta['jvm_artifact']);
|
181
|
-
}
|
182
|
-
return build_link(tUrl, value);
|
183
|
-
}
|
184
|
-
},
|
185
|
-
MESOS_TERM_DEBUG_GRANTED_TO: groups_decorator,
|
186
|
-
OWNERS: groups_decorator,
|
187
|
-
marathon_app_id: function(instance, key, value, serviceName) {
|
188
|
-
var app_name = value;
|
189
|
-
if (app_name[0] != '/') {
|
190
|
-
app_name = '/' + app_name;
|
191
|
-
}
|
192
|
-
if (instance.sMeta && instance.sMeta['marathon_ui']) {
|
193
|
-
return build_link(instance.sMeta['marathon_ui'] + '/#/apps/' + encodeURIComponent(app_name), value);
|
194
|
-
} else {
|
195
|
-
// non decorated value
|
196
|
-
return document.createTextNode(value);
|
197
|
-
}
|
198
|
-
},
|
199
|
-
marathon_app_version: function(instance, key, value, serviceName) {
|
200
|
-
return decorateIsoDate(value, value);
|
201
|
-
},
|
202
|
-
slack_channel: slack_channel,
|
203
|
-
start: function(instance, key, value, serviceName) {
|
204
|
-
const reg = start_regexp.exec(value);
|
205
|
-
if (reg != null) {
|
206
|
-
var formated = reg[1] + '-' + reg[2] + '-' + reg[3] + 'T' + reg[4] + ':' + reg[5] + ':' + reg[6] + 'Z';
|
207
|
-
return decorateIsoDate(value, formated);
|
208
|
-
} else {
|
209
|
-
return document.createTextNode(value);
|
210
|
-
}
|
211
|
-
},
|
212
|
-
swagger_key: function(instance, key, value, serviceName) {
|
213
|
-
return build_link(swagger_url + encodeURIComponent(value), value);
|
214
|
-
},
|
215
120
|
}
|
216
121
|
|
217
122
|
function service_meta_semantics_decorator(instance, key, value, serviceName) {
|
@@ -286,85 +191,11 @@ function nodeMetaGenerator(nodeMetaTags, serviceMetaIfAny) {
|
|
286
191
|
* it does not have to return anything.
|
287
192
|
*/
|
288
193
|
function navBarDecorator(navbar) {
|
289
|
-
if (typeof consulManager === 'undefined') {
|
290
|
-
// Timepicker is not supported on this page
|
291
|
-
return;
|
292
|
-
}
|
293
|
-
var timepicker_container = document.createElement('div');
|
294
|
-
timepicker_container.innerHTML = `
|
295
|
-
<div class="row">
|
296
|
-
<button type="button" disabled class="btn btn-secondary" data-toggle="tooltip" data-html="true" title="Data returned is the closest available to request" id="currently-displayed-data-date">
|
297
|
-
Pick a date to see data from the past
|
298
|
-
</button>
|
299
|
-
<div class="col-sm-14">
|
300
|
-
<div class="form-group">
|
301
|
-
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
|
302
|
-
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1"/>
|
303
|
-
<div class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
|
304
|
-
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
|
305
|
-
</div>
|
306
|
-
</div>
|
307
|
-
</div>
|
308
|
-
</div>
|
309
|
-
</div>
|
310
|
-
`;
|
311
|
-
|
312
|
-
navbar.appendChild(timepicker_container);
|
313
|
-
var script= document.createElement('script');
|
314
|
-
script.type='text/javascript';
|
315
|
-
// TODO(g.seux): extract code to another file and set "source" on script element
|
316
|
-
script.innerHTML = `
|
317
|
-
$('#datetimepicker1').datetimepicker({
|
318
|
-
format: 'DD/MM/YYYY HH:mm:ss Z', // default format does not allow to select seconds
|
319
|
-
sideBySide: true, // display date+time on the same widget
|
320
|
-
useCurrent: true // by default, select current date
|
321
|
-
});
|
322
|
-
$("#datetimepicker1").on("change.datetimepicker", function (e) {
|
323
|
-
console.log("Will fetch data from date " + e.date);
|
324
|
-
console.log("will clean existing data");
|
325
|
-
consulManager.clean().then(function(result) {
|
326
|
-
switch(consulManager.constructor.name) {
|
327
|
-
case "ConsulServiceManager":
|
328
|
-
backup_type = 'consul_services';
|
329
|
-
break;
|
330
|
-
case "ConsulKeysManager":
|
331
|
-
backup_type = 'consul_keys'
|
332
|
-
break;
|
333
|
-
case "ConsulNodesManager":
|
334
|
-
backup_type = 'consul_nodes'
|
335
|
-
break;
|
336
|
-
default:
|
337
|
-
console.log("Unknown " + consulManager.constructor.name + " type");
|
338
|
-
}
|
339
|
-
if (e.date) {
|
340
|
-
console.log("Will replace data by closest snapshot to " + e.date);
|
341
|
-
var target_url = "https://consul-info-timeline-history.<%= ENV['CRITEO_DC'] %>.<%= ENV['CRITEO_ENV']%>.crto.in/backup/" + backup_type + "/" + e.date / 1000;
|
342
|
-
} else {
|
343
|
-
console.log("Will restore to local version");
|
344
|
-
var target_url = defaultConsulManager.resourceURL;
|
345
|
-
}
|
346
|
-
if (typeof(defaultConsulManager) == 'undefined') {
|
347
|
-
// store first manager to be able to restore it
|
348
|
-
defaultConsulManager = consulManager
|
349
|
-
}
|
350
|
-
consulManager = new consulManager.constructor(target_url);
|
351
|
-
});
|
352
|
-
});
|
353
|
-
`;
|
354
|
-
navbar.appendChild(script);
|
355
194
|
}
|
356
195
|
|
357
|
-
|
358
196
|
/**
|
359
197
|
* fetchedResponseDecorator is called with http response when a resource is fetched by any instance of ConsulUIManager
|
360
198
|
* it does not have to return anything.
|
361
199
|
*/
|
362
200
|
async function fetchedResponseDecorator(httpResponse) {
|
363
|
-
|
364
|
-
current_date_display = $('#currently-displayed-data-date');
|
365
|
-
if (data_date > 0) {
|
366
|
-
current_date_display.html("Data is a snapshot from: " + moment.unix(data_date).format('DD/MM/YYYY HH:mm:ss Z'));
|
367
|
-
} else {
|
368
|
-
current_date_display.html("Pick a date to see data from the past");
|
369
|
-
}
|
370
|
-
}
|
201
|
+
}
|
@@ -111,7 +111,7 @@ class NodeMainSelector extends MainSelector {
|
|
111
111
|
nodesChecks.appendChild(checksStatusGenerator(node, node['Node']['Name']));
|
112
112
|
content.appendChild(nodesChecks);
|
113
113
|
|
114
|
-
content.appendChild(servicesGenerator(node['Service']));
|
114
|
+
content.appendChild(servicesGenerator(node['Service'], node));
|
115
115
|
content.appendChild(tagsGenerator(getTagsNode(node)));
|
116
116
|
|
117
117
|
sidebar.setAttribute('status', state);
|
@@ -304,7 +304,7 @@ class ServiceMainSelector extends MainSelector {
|
|
304
304
|
element.setAttribute("class", "list-group-item service-instance");
|
305
305
|
var state = nodeState(instance.checks);
|
306
306
|
element.appendChild(weightsGenerator(instance.weights, state));
|
307
|
-
element.appendChild(serviceTitleGenerator(instance));
|
307
|
+
element.appendChild(serviceTitleGenerator(instance, serviceName));
|
308
308
|
var node_info = this.nodes[instance.name];
|
309
309
|
if (node_info != null) {
|
310
310
|
node_info = node_info.meta;
|
@@ -63,7 +63,7 @@ function nodeState(checks) {
|
|
63
63
|
|
64
64
|
supported_protocols = ['https', 'http', 'sftp', 'ftp', 'ssh', 'telnet']
|
65
65
|
|
66
|
-
function serviceTitleGenerator(instance) {
|
66
|
+
function serviceTitleGenerator(instance, serviceName) {
|
67
67
|
var protocol = null;
|
68
68
|
for (i in supported_protocols) {
|
69
69
|
var protoc = supported_protocols[i]
|
@@ -74,10 +74,6 @@ function serviceTitleGenerator(instance) {
|
|
74
74
|
}
|
75
75
|
|
76
76
|
var htmlTitle = document.createElement('h5');
|
77
|
-
htmlTitle.setAttribute('title', 'Node Name: ' + instance.name +
|
78
|
-
'\nAddress: ' + instance.addr +
|
79
|
-
'\nService ID: ' + instance.id +
|
80
|
-
'\nService Port: ' + instance.port);
|
81
77
|
|
82
78
|
htmlTitle.setAttribute('class', 'instance-name');
|
83
79
|
var instanceLink = document.createElement('a');
|
@@ -91,6 +87,15 @@ function serviceTitleGenerator(instance) {
|
|
91
87
|
}
|
92
88
|
|
93
89
|
instanceLink.appendChild(document.createTextNode(instance.name + appendPort));
|
90
|
+
const nodeInfo = document.createElement('a');
|
91
|
+
nodeInfo.appendChild(document.createTextNode('\u24D8'));
|
92
|
+
nodeInfo.setAttribute('title', 'Click to see details of Node: ' + instance.name +
|
93
|
+
'\nAddress: ' + instance.addr +
|
94
|
+
'\nService ID: ' + instance.id +
|
95
|
+
'\nService Port: ' + instance.port);
|
96
|
+
nodeInfo.setAttribute('href', 'consul-nodes-ui.html?node_filter=^' + encodeURIComponent(instance.name) + '$');
|
97
|
+
htmlTitle.appendChild(nodeInfo);
|
98
|
+
htmlTitle.appendChild(document.createTextNode(' '));
|
94
99
|
htmlTitle.appendChild(instanceLink);
|
95
100
|
htmlTitle.appendChild(document.createTextNode(' '));
|
96
101
|
htmlTitle.appendChild(document.createTextNode(instance.addr));
|
@@ -216,24 +221,44 @@ function toCSSClass(state) {
|
|
216
221
|
return state;
|
217
222
|
}
|
218
223
|
|
219
|
-
function servicesGenerator(instanceServices) {
|
220
|
-
var
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
+
function servicesGenerator(instanceServices, node) {
|
225
|
+
var servicesTop = document.createElement('div');
|
226
|
+
servicesTop.className = 'instance-services';
|
227
|
+
const card = document.createElement('div');
|
228
|
+
card.setAttribute('class', 'card');
|
229
|
+
const servicesCard = document.createElement('div');
|
230
|
+
servicesCard.setAttribute('class', 'card-body');
|
231
|
+
const title = document.createElement('h5');
|
232
|
+
title.appendChild(document.createTextNode('Services'));
|
233
|
+
title.setAttribute('class', 'card-title')
|
234
|
+
servicesCard.appendChild(title);
|
235
|
+
const services = document.createElement('div');
|
236
|
+
servicesCard.appendChild(services);
|
224
237
|
for (var serviceKey in instanceServices) {
|
225
|
-
|
226
|
-
|
238
|
+
const serviceGrp = document.createElement('span');
|
239
|
+
serviceGrp.setAttribute('class', 'btn btn-sm');
|
240
|
+
serviceGrp.classList.add('btn-outline-' + toCSSClass(nodeState(instanceServices[serviceKey]['Checks'])))
|
241
|
+
const service = document.createElement('a');
|
242
|
+
serviceGrp.appendChild(service);
|
243
|
+
const serviceName = instanceServices[serviceKey]['Service']['Service'];
|
244
|
+
service.setAttribute('class', 'serviceLink');
|
245
|
+
service.setAttribute('href', 'consul-services-ui.html?service=' + encodeURIComponent(serviceName) + '&node_filter=^' + encodeURIComponent(node['Node']['Name'])+'$');
|
246
|
+
service.appendChild(document.createTextNode(serviceName));
|
227
247
|
var servicePort = instanceServices[serviceKey]['Service']['Port'];
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
248
|
+
if (servicePort) {
|
249
|
+
// Add unbreakable space
|
250
|
+
serviceGrp.appendChild(document.createTextNode('\u00A0'));
|
251
|
+
const servicePortElem = document.createElement('a');
|
252
|
+
servicePortElem.setAttribute('class', 'serviceTargetPort');
|
253
|
+
const nodeAddr = instanceServices[serviceKey]['Service']['Address'];
|
254
|
+
servicePortElem.setAttribute('href', 'http://' + nodeAddr + ':' + servicePort);
|
255
|
+
servicePortElem.appendChild(document.createTextNode(':' + servicePort));
|
256
|
+
serviceGrp.appendChild(servicePortElem);
|
257
|
+
}
|
258
|
+
services.appendChild(serviceGrp);
|
235
259
|
}
|
236
|
-
|
260
|
+
servicesTop.appendChild(servicesCard);
|
261
|
+
return servicesTop;
|
237
262
|
}
|
238
263
|
|
239
264
|
function checksStatusGenerator(instance, prefix) {
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%
|
2
|
+
# This example show how to display local time informaition about changes
|
3
|
+
# Example of usage to display logs of changes on nodes() endpoint:
|
4
|
+
#
|
5
|
+
# consul-templaterb --template "display_timestamped_changes.txt.erb:display_timestamped_changes.txt:cat display_timestamped_changes.txt" -l error
|
6
|
+
#
|
7
|
+
# Would output:
|
8
|
+
# Last update: 1588800554 (2020-05-06 21:29:14 UTC), X-Consul-Index: 4345827328
|
9
|
+
# Last update: 1588800569 (2020-05-06 21:29:29 UTC), X-Consul-Index: 4345829548
|
10
|
+
# Last update: 1588800676 (2020-05-06 21:31:16 UTC), X-Consul-Index: 4345836342
|
11
|
+
#
|
12
|
+
@my_last_time = Time.now.utc unless @my_last_time
|
13
|
+
val = nodes()
|
14
|
+
new_idx = val.endpoint.x_consul_index
|
15
|
+
@my_last_time = Time.now.utc if @my_last_idx != new_idx
|
16
|
+
@my_last_idx = new_idx
|
17
|
+
%>Last update: <%= @my_last_time.to_i %> (<%= @my_last_time %>), X-Consul-Index: <%= new_idx %>
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# HELP consul_wan_servers_rtt_seconds Min Round trip with other servers of DC
|
2
|
+
# TYPE consul_wan_servers_rtt_seconds gauge
|
3
|
+
<%
|
4
|
+
# This computes the RTT over WAN for all DCs
|
5
|
+
results = {'min' => {}, 'max' => {}, 'p50' => {}, 'p90' => {}}
|
6
|
+
my_dcname = (agent_self['Config'] || {})['Datacenter']
|
7
|
+
my_dc = coordinate.datacenters.select { |d| d['Datacenter'] == my_dcname }.first
|
8
|
+
|
9
|
+
def percentile(values_sorted, percentile)
|
10
|
+
k = (percentile*(values_sorted.length-1)+1).floor - 1
|
11
|
+
f = (percentile*(values_sorted.length-1)+1).modulo(1)
|
12
|
+
return values_sorted[k] + (f * (values_sorted[k+1] - values_sorted[k]))
|
13
|
+
end
|
14
|
+
|
15
|
+
if my_dc
|
16
|
+
from = my_dc['Datacenter']
|
17
|
+
coordinate.datacenters.each do |dc_coords|
|
18
|
+
min = Float::MAX
|
19
|
+
max = 0
|
20
|
+
rtts = []
|
21
|
+
dc_coords['Coordinates'].each do |s2|
|
22
|
+
my_dc['Coordinates'].each do |s1|
|
23
|
+
rtt = coordinate.rtt(s1['Coord'], s2['Coord'])
|
24
|
+
min = rtt if rtt < min
|
25
|
+
max = rtt if rtt > max
|
26
|
+
idx = rtts.bsearch_index { |x| x > rtt }
|
27
|
+
if idx.nil?
|
28
|
+
rtts << rtt
|
29
|
+
else
|
30
|
+
rtts = rtts.insert(idx, rtt)
|
31
|
+
end
|
32
|
+
%>
|
33
|
+
consul_wan_servers_rtt_seconds{from="<%= from %>",to="<%= dc_coords['Datacenter'] %>",from_node="<%= s1['Node'] %>",to_node="<%= s2['Node'] %>"} <%= rtt.round(3) %><%
|
34
|
+
end
|
35
|
+
end
|
36
|
+
results['min'][dc_coords['Datacenter']] = min
|
37
|
+
results['max'][dc_coords['Datacenter']] = max
|
38
|
+
results['p50'][dc_coords['Datacenter']] = percentile(rtts, 0.5)
|
39
|
+
results['p90'][dc_coords['Datacenter']] = percentile(rtts, 0.9)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Now, we iterate over aggregated results, type is min, max...
|
43
|
+
results.each do |type, res_type|
|
44
|
+
%>
|
45
|
+
|
46
|
+
# HELP consul_wan_dc_<%= type %>_seconds <%= type %> round trip with other DCs
|
47
|
+
# TYPE consul_wan_dc_<%= type %>_seconds gauge
|
48
|
+
<%
|
49
|
+
# Iterate over destinations
|
50
|
+
res_type.each do |dst, val|
|
51
|
+
%>consul_wan_dc_<%= type %>_seconds{from="<%= my_dcname %>",to="<%= dst %>"} <%= val.round(3) %>
|
52
|
+
<%
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
%>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consul-templaterb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.28.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SRE Core Services
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-http-request
|
@@ -198,6 +198,7 @@ files:
|
|
198
198
|
- samples/all_services.txt.erb
|
199
199
|
- samples/all_services_multi_agents.txt.erb
|
200
200
|
- samples/all_templates.erb
|
201
|
+
- samples/checks_in_warning_or_critical_state.yaml.erb
|
201
202
|
- samples/consul-ui/README.md
|
202
203
|
- samples/consul-ui/common/footer.html.erb
|
203
204
|
- samples/consul-ui/common/header.html.erb
|
@@ -229,6 +230,7 @@ files:
|
|
229
230
|
- samples/criteo/haproxy.cfg.erb
|
230
231
|
- samples/debug/compare_connect_services.txt.erb
|
231
232
|
- samples/demos/compute_pricing.txt.erb
|
233
|
+
- samples/display_timestamped_changes.txt.erb
|
232
234
|
- samples/find_all_invalid_dns_labels.json.erb
|
233
235
|
- samples/find_nodes_in_catalog_but_not_in_members.json.erb
|
234
236
|
- samples/ha_proxy.cfg.erb
|
@@ -240,6 +242,7 @@ files:
|
|
240
242
|
- samples/members.json.erb
|
241
243
|
- samples/metrics.erb
|
242
244
|
- samples/prometheus_consul_coordinates.erb
|
245
|
+
- samples/prometheus_datacenter_coordinates.erb
|
243
246
|
- samples/render_template_from_kv.erb
|
244
247
|
- samples/sample_keys.html.erb
|
245
248
|
- samples/service_checks_metrics.erb
|
@@ -270,8 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
273
|
- !ruby/object:Gem::Version
|
271
274
|
version: '0'
|
272
275
|
requirements: []
|
273
|
-
|
274
|
-
rubygems_version: 2.7.7
|
276
|
+
rubygems_version: 3.0.8
|
275
277
|
signing_key:
|
276
278
|
specification_version: 4
|
277
279
|
summary: Implementation of Consul template using Ruby and .erb templating language
|