consul-templaterb 1.26.0 → 1.27.1
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 +40 -2
- data/Dockerfile +1 -1
- data/README.md +1 -1
- data/TemplateAPI.md +9 -0
- data/bin/consul-templaterb +30 -0
- data/docs/article-06_Template-based_discovery_with_consul-templaterb.md +2 -1
- data/lib/consul/async/consul_endpoint.rb +19 -3
- data/lib/consul/async/consul_template.rb +39 -21
- data/lib/consul/async/json_endpoint.rb +15 -2
- 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/README.md +19 -0
- data/samples/consul-ui/decorators.js.erb +2 -171
- data/samples/display_timestamped_changes.txt.erb +17 -0
- data/samples/find_all_invalid_dns_labels.json.erb +12 -0
- data/samples/find_nodes_in_catalog_but_not_in_members.json.erb +28 -0
- data/samples/metrics.erb +14 -0
- data/samples/prometheus_datacenter_coordinates.erb +56 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c4d19fa22c559f4a7960529dd3960c1c741f631b1cf0b51f5d10155b2b8a131
|
4
|
+
data.tar.gz: 926930e64938a40c2a2653abf9a84f0c86057201646a2f8e02fed06ed51927b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5126d79bcbd0d469bcb7ec5d214c979990056c3c11e1547d4819849f3fcd7fddd1534354f565722a52dbb09ba26c63dd4553f40282fcafaccd7be6a2a1d9442f
|
7
|
+
data.tar.gz: 7d6345911c69a8c0622f0b88343968ff951d8243db78d41b8cfad74e26ad5c87a39ae02e3bb24d4cf1615fe329fc53642f81aaf76591d1f5be4098c576baea96
|
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: 182
|
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,16 +2,54 @@
|
|
2
2
|
|
3
3
|
## (UNRELEASED)
|
4
4
|
|
5
|
+
## 1.27.1 (July 28 2020)
|
6
|
+
|
7
|
+
BUGIX:
|
8
|
+
|
9
|
+
* Fix collision in JSON queries when using payload in requests #68
|
10
|
+
|
11
|
+
## 1.27.0 (June 5, 2020)
|
12
|
+
|
13
|
+
NEW FEATURES:
|
14
|
+
|
15
|
+
* For Consul 1.7+, now support `checks_in_state(check_state, dc: nil, [agent: consul_agent_address])`,
|
16
|
+
fixes feature [#65](https://github.com/criteo/consul-templaterb/issues/65)
|
17
|
+
* New options to support/disable TLS validation thanks to [@jeromegn](https://github.com/jeromegn)
|
18
|
+
[#66](https://github.com/criteo/consul-templaterb/pull/66)
|
19
|
+
|
20
|
+
## 1.26.3 (April 15, 2020)
|
21
|
+
|
22
|
+
BUGFIX:
|
23
|
+
|
24
|
+
* Removed all Criteo specific decorators from Consul-UI
|
25
|
+
|
26
|
+
## 1.26.2 (April 15, 2020)
|
27
|
+
|
28
|
+
BUGFIX:
|
29
|
+
|
30
|
+
* Fixed broken Dockerfile (was missing the new `decorator.js.erb` file). Fixes #61 (Thanks to @ simongareste)
|
31
|
+
|
5
32
|
NEW FEATURES:
|
6
33
|
|
34
|
+
* Added `consul_members_count` metric in [samples/metrics.erb](samples/metrics.erb)
|
35
|
+
|
36
|
+
## 1.26.1 (March 27, 2020)
|
37
|
+
|
38
|
+
BUGFIX:
|
39
|
+
|
40
|
+
* Using `agent: http://vault_or_consul_agent:port>` was not properly taken into account in some endpoints
|
41
|
+
|
7
42
|
## 1.26.0 (March 5, 2020)
|
8
43
|
|
44
|
+
NEW FEATURES:
|
45
|
+
|
9
46
|
* Using `agent: http://vault_or_consul_agent:port>` on most methods will now override the agent
|
10
47
|
used to perform Consul queries. It might be useful for very large clusters or large WAN federations,
|
11
48
|
because you can perform some requests on some agents, and some others on other agenrs. Might
|
12
49
|
also be useful to federate data from preprod/prod for instance.
|
13
|
-
|
14
|
-
|
50
|
+
* Added agent attribute for all methods in documentation
|
51
|
+
* Added [samples/all_services_multi_agents.txt.erb](samples/all_services_multi_agents.txt.erb) as an
|
52
|
+
example of new feature.
|
15
53
|
|
16
54
|
## 1.25.2 (February 29, 2020)
|
17
55
|
|
data/Dockerfile
CHANGED
@@ -12,4 +12,4 @@ ENV LANG C.UTF-8
|
|
12
12
|
ENV CONSUL_HTTP_ADDR http://consul-relay.service.consul.preprod.crto.in:8500
|
13
13
|
|
14
14
|
ENTRYPOINT ["/usr/local/bin/bundle", "exec", "consul-templaterb"]
|
15
|
-
CMD ["--template", "samples/consul-ui/consul-keys-ui.html.erb", "--template", "samples/consul-ui/consul-nodes-ui.html.erb", "--template", "samples/consul-ui/consul-services-ui.html.erb", "--template", "samples/consul-ui/consul-timeline-ui.html.erb", "--template", "samples/consul-ui/consul_keys.json.erb", "--template", "samples/consul-ui/consul_nodes.json.erb", "--template", "samples/consul-ui/consul_services.json.erb", "--template", "samples/consul-ui/timeline.json.erb", "--template", "samples/consul-ui/consul-services-ui.html.erb:samples/consul-ui/index.html:touch samples/consul-ui/ready", "--sig-reload=NONE", "--exec=nginx -c /usr/src/app/docker-nginx-conf/nginx.conf"]
|
15
|
+
CMD ["--template", "samples/consul-ui/consul-keys-ui.html.erb", "--template", "samples/consul-ui/decorators.js.erb", "--template", "samples/consul-ui/consul-nodes-ui.html.erb", "--template", "samples/consul-ui/consul-services-ui.html.erb", "--template", "samples/consul-ui/consul-timeline-ui.html.erb", "--template", "samples/consul-ui/consul_keys.json.erb", "--template", "samples/consul-ui/consul_nodes.json.erb", "--template", "samples/consul-ui/consul_services.json.erb", "--template", "samples/consul-ui/timeline.json.erb", "--template", "samples/consul-ui/consul-services-ui.html.erb:samples/consul-ui/index.html:touch samples/consul-ui/ready", "--sig-reload=NONE", "--exec=nginx -c /usr/src/app/docker-nginx-conf/nginx.conf"]
|
data/README.md
CHANGED
@@ -332,7 +332,7 @@ Please consult [CHANGELOG.md](CHANGELOG.md) for fixed bugs.
|
|
332
332
|
|
333
333
|
## TODO
|
334
334
|
|
335
|
-
* [x] Hashi's Vault support
|
335
|
+
* [x] Hashi's Vault support
|
336
336
|
* [ ] Implement automatic dynamic rate limit
|
337
337
|
* [x] More samples: apache, nginx, a full website displaying consul information...
|
338
338
|
* [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
|
@@ -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
|
@@ -120,4 +120,5 @@ If you don’t want to bother configuring and tuning it, you can try it very qui
|
|
120
120
|
* [Mixing Observability with Service Discovery](https://medium.com/criteo-labs/mixing-observability-with-service-discovery-2bb8909e8530)
|
121
121
|
|
122
122
|
Pierre Souchay, 2020-03-03
|
123
|
-
|
123
|
+
|
124
|
+
Also published on https://medium.com/criteo-labs/template-based-discovery-with-consul-templaterb-8ff88434c457
|
@@ -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
|
@@ -53,7 +54,7 @@ module Consul
|
|
53
54
|
query_params = {}
|
54
55
|
query_params[:dc] = dc if dc
|
55
56
|
@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
|
+
ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]', agent))
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
@@ -63,7 +64,7 @@ module Consul
|
|
63
64
|
query_params = {}
|
64
65
|
query_params[:dc] = dc if dc
|
65
66
|
@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
|
+
ConsulTemplateNodes.new(ConsulEndpoint.new(@endp_manager.consul_conf, path, true, query_params, '[]', agent))
|
67
68
|
end
|
68
69
|
end
|
69
70
|
|
@@ -136,7 +137,7 @@ module Consul
|
|
136
137
|
query_params[:dc] = dc if dc
|
137
138
|
query_params[:passing] = passing if passing
|
138
139
|
query_params[:tag] = tag if tag
|
139
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateService.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
140
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateService.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
140
141
|
end
|
141
142
|
|
142
143
|
# https://www.consul.io/api/health.html#list-checks-for-service
|
@@ -147,7 +148,7 @@ module Consul
|
|
147
148
|
query_params = {}
|
148
149
|
query_params[:dc] = dc if dc
|
149
150
|
query_params[:passing] = passing if passing
|
150
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
151
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
151
152
|
end
|
152
153
|
|
153
154
|
# https://www.consul.io/api/health.html#list-checks-for-node
|
@@ -158,7 +159,19 @@ module Consul
|
|
158
159
|
query_params = {}
|
159
160
|
query_params[:dc] = dc if dc
|
160
161
|
query_params[:passing] = passing if passing
|
161
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
162
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
163
|
+
end
|
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)) }
|
162
175
|
end
|
163
176
|
|
164
177
|
# https://www.consul.io/api/catalog.html#list-nodes
|
@@ -166,7 +179,7 @@ module Consul
|
|
166
179
|
path = '/v1/catalog/nodes'
|
167
180
|
query_params = {}
|
168
181
|
query_params[:dc] = dc if dc
|
169
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
182
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
170
183
|
end
|
171
184
|
|
172
185
|
# https://www.consul.io/api/catalog.html#list-services-for-node
|
@@ -174,7 +187,7 @@ module Consul
|
|
174
187
|
path = "/v1/catalog/node/#{name_or_id}"
|
175
188
|
query_params = {}
|
176
189
|
query_params[:dc] = dc if dc
|
177
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
|
190
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}', agent)) }
|
178
191
|
end
|
179
192
|
|
180
193
|
# https://www.consul.io/api/agent.html#read-configuration
|
@@ -182,7 +195,7 @@ module Consul
|
|
182
195
|
path = '/v1/agent/self'
|
183
196
|
query_params = {}
|
184
197
|
default_value = '{"Config":{}, "Coord":{}, "Member":{}, "Meta":{}, "Stats":{}}'
|
185
|
-
create_if_missing(path, query_params, agent: agent) { ConsulAgentSelf.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
|
198
|
+
create_if_missing(path, query_params, agent: agent) { ConsulAgentSelf.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
|
186
199
|
end
|
187
200
|
|
188
201
|
# https://www.consul.io/api/agent.html#view-metrics
|
@@ -190,7 +203,7 @@ module Consul
|
|
190
203
|
path = '/v1/agent/metrics'
|
191
204
|
query_params = {}
|
192
205
|
default_value = '{"Gauges":[], "Points":[], "Member":{}, "Counters":[], "Samples":{}}'
|
193
|
-
create_if_missing(path, query_params, agent: agent) { ConsulAgentMetrics.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
|
206
|
+
create_if_missing(path, query_params, agent: agent) { ConsulAgentMetrics.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
|
194
207
|
end
|
195
208
|
|
196
209
|
# https://www.consul.io/api/agent.html#list-members
|
@@ -199,7 +212,7 @@ module Consul
|
|
199
212
|
query_params = {}
|
200
213
|
query_params['wan'] = true if wan
|
201
214
|
default_value = '[]'
|
202
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateMembers.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
|
215
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateMembers.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent)) }
|
203
216
|
end
|
204
217
|
|
205
218
|
# Return a param of template
|
@@ -222,14 +235,14 @@ module Consul
|
|
222
235
|
query_params[:dc] = dc if dc
|
223
236
|
# Tag filtering is performed on client side
|
224
237
|
query_params[:tag] = tag if tag
|
225
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateServices.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
|
238
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateServices.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}', agent)) }
|
226
239
|
end
|
227
240
|
|
228
241
|
# https://www.consul.io/api/catalog.html#list-datacenters
|
229
242
|
def datacenters(agent: nil)
|
230
243
|
path = '/v1/catalog/datacenters'
|
231
244
|
query_params = {}
|
232
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
245
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]', agent)) }
|
233
246
|
end
|
234
247
|
|
235
248
|
# https://www.consul.io/api/kv.html#read-key
|
@@ -240,7 +253,7 @@ module Consul
|
|
240
253
|
query_params[:recurse] = recurse if recurse
|
241
254
|
query_params[:keys] = keys if keys
|
242
255
|
default_value = '[]'
|
243
|
-
create_if_missing(path, query_params, agent: agent) { ConsulTemplateKV.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value), name) }
|
256
|
+
create_if_missing(path, query_params, agent: agent) { ConsulTemplateKV.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value, agent), name) }
|
244
257
|
end
|
245
258
|
|
246
259
|
def secrets(path = '', agent: nil)
|
@@ -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
|
@@ -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
|
}
|
@@ -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
|
+
%>
|
data/samples/consul-ui/README.md
CHANGED
@@ -50,6 +50,25 @@ The content is statically created, so you can serve it using any HTTP server ver
|
|
50
50
|
For development, you might use `python -m SimpleHTTPServer` in order to server the result
|
51
51
|
on your browser.
|
52
52
|
|
53
|
+
### Running it in production
|
54
|
+
|
55
|
+
Whatever your solution, be sure to have a index.html, so read next below on
|
56
|
+
how generating an index.html.
|
57
|
+
|
58
|
+
#### Running with web server
|
59
|
+
|
60
|
+
You can run it with your favoite web server, at Criteo we run it with nginx
|
61
|
+
which handles nicely cache and offer good performance.
|
62
|
+
|
63
|
+
In that case, consul-templaterb can start nginx with `--exec` or you can run it
|
64
|
+
as a daemon, in which case, consul-templaterb only generate the files.
|
65
|
+
|
66
|
+
#### Run it in Consul
|
67
|
+
|
68
|
+
You can run consul with `-ui-dir=/path/to/directory/of/consul-ui`, in such case
|
69
|
+
reaching consul on poort 8500 will redirect to the /ui/ path, displaying the UI
|
70
|
+
of your choice on http://consul-agent.example.org:8500/ui/.
|
71
|
+
|
53
72
|
### Generating index.html
|
54
73
|
|
55
74
|
By default, the command `consul-templaterb -c http://localhost:8500 samples/consul-ui/*.erb``
|
@@ -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
|
+
}
|
@@ -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,12 @@
|
|
1
|
+
<%
|
2
|
+
r = /^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,62}[a-zA-Z0-9])?$/
|
3
|
+
invalid_services = {}
|
4
|
+
services.each do |servive_name, tags|
|
5
|
+
errors = []
|
6
|
+
errors << "Invalid service name #{servive_name}" unless r.match(servive_name)
|
7
|
+
tags.each do |tag|
|
8
|
+
errors << "Invalid tag #{tag}" unless r.match(tag)
|
9
|
+
end
|
10
|
+
invalid_services[servive_name] = errors unless errors.empty?
|
11
|
+
end
|
12
|
+
%><%= JSON.pretty_generate({"errors": invalid_services.size, "details": invalid_services}) %>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%=
|
2
|
+
all_catalog = nodes.map{ |n| [n['Node'], n] }.to_h
|
3
|
+
all_members = agent_members.sort{ |a,b| a['Name'] <=> b['Name'] }
|
4
|
+
members_by_state = {}
|
5
|
+
all_members.each do |m|
|
6
|
+
state = m.status
|
7
|
+
members = members_by_state[state] || 0
|
8
|
+
members_by_state[state] = members + 1
|
9
|
+
end
|
10
|
+
# Find all virtual nodes (eg: k8s-sync)
|
11
|
+
catalog_external_sources = nodes.select{|n| (n['Meta']||{})['external-source'] }.map{|n| n['Node']}
|
12
|
+
in_catalog_but_not_in_members = all_catalog.keys - all_members.map{ |m| m['Name'] }
|
13
|
+
not_virtual_not_in_members = in_catalog_but_not_in_members - catalog_external_sources
|
14
|
+
JSON.pretty_generate(
|
15
|
+
{
|
16
|
+
'stats' => {
|
17
|
+
'catalog' => all_catalog.count,
|
18
|
+
'catalog_virtual' => in_catalog_but_not_in_members.count,
|
19
|
+
'members' => all_members.count,
|
20
|
+
'members_by_state': members_by_state,
|
21
|
+
'catalog_external_sources' => catalog_external_sources,
|
22
|
+
},
|
23
|
+
'errors': {
|
24
|
+
'in_catalog_but_not_in_members' => in_catalog_but_not_in_members,
|
25
|
+
'not_virtual_not_in_members' => not_virtual_not_in_members,
|
26
|
+
}
|
27
|
+
})
|
28
|
+
%>
|
data/samples/metrics.erb
CHANGED
@@ -21,6 +21,20 @@
|
|
21
21
|
all_stats = {}
|
22
22
|
service_count = 0
|
23
23
|
|
24
|
+
statuses = agent_members().map {|m| m.status}.reduce({}) do |sum, s|
|
25
|
+
v = sum[s] || 0
|
26
|
+
sum[s] = v + 1
|
27
|
+
sum
|
28
|
+
end
|
29
|
+
%>
|
30
|
+
# HELP consul_members_count A gauge of number of serf members with their count and their status('alive', 'leaving', 'left', 'failed')
|
31
|
+
# TYPE consul_members_count gauge
|
32
|
+
<%
|
33
|
+
statuses.each do |k, v|
|
34
|
+
%>
|
35
|
+
consul_members_count{serf="lan",status="<%= k %>"} <%= v %><%
|
36
|
+
end
|
37
|
+
|
24
38
|
services(tag: service_tag_filter).each do |service_name, tags|
|
25
39
|
if !services_blacklist.any? {|r| r.match(service_name)} && (instance_must_tag.nil? || tags.include?(instance_must_tag))
|
26
40
|
service_count += 1
|
@@ -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.27.1
|
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-07-28 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,9 @@ 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
|
234
|
+
- samples/find_all_invalid_dns_labels.json.erb
|
235
|
+
- samples/find_nodes_in_catalog_but_not_in_members.json.erb
|
232
236
|
- samples/ha_proxy.cfg.erb
|
233
237
|
- samples/haproxy_dns.cfg.erb
|
234
238
|
- samples/hosts.erb
|
@@ -238,6 +242,7 @@ files:
|
|
238
242
|
- samples/members.json.erb
|
239
243
|
- samples/metrics.erb
|
240
244
|
- samples/prometheus_consul_coordinates.erb
|
245
|
+
- samples/prometheus_datacenter_coordinates.erb
|
241
246
|
- samples/render_template_from_kv.erb
|
242
247
|
- samples/sample_keys.html.erb
|
243
248
|
- samples/service_checks_metrics.erb
|
@@ -268,8 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
273
|
- !ruby/object:Gem::Version
|
269
274
|
version: '0'
|
270
275
|
requirements: []
|
271
|
-
|
272
|
-
rubygems_version: 2.7.7
|
276
|
+
rubygems_version: 3.0.8
|
273
277
|
signing_key:
|
274
278
|
specification_version: 4
|
275
279
|
summary: Implementation of Consul template using Ruby and .erb templating language
|