consul-templaterb 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/bin/consul-templaterb +43 -4
- data/lib/consul/async/consul_endpoint.rb +4 -34
- data/lib/consul/async/consul_template.rb +54 -13
- data/lib/consul/async/consul_template_engine.rb +2 -1
- data/lib/consul/async/endpoint.rb +137 -0
- data/lib/consul/async/stats.rb +40 -0
- data/lib/consul/async/vault_endpoint.rb +249 -0
- data/lib/consul/async/version.rb +1 -1
- data/null/ruby-type-inference/ruby-type-inference.mv.db +0 -0
- data/samples/consul-ui/common/header.html.erb +20 -1
- data/samples/consul-ui/consul-keys-ui.html.erb +39 -0
- data/samples/consul-ui/consul-nodes-ui.html.erb +35 -0
- data/samples/consul-ui/consul-services-ui.html.erb +9 -3
- data/samples/consul-ui/consul_keys.json.erb +12 -0
- data/samples/consul-ui/consul_nodes.json.erb +64 -0
- data/samples/consul-ui/{consul_template.json.erb → consul_services.json.erb} +0 -0
- data/samples/consul-ui/css/style.css +80 -4
- data/samples/consul-ui/js/keys.js +129 -0
- data/samples/consul-ui/js/nodes.js +120 -0
- data/samples/consul-ui/js/service.js +53 -179
- data/samples/consul-ui/js/utils.js +347 -0
- data/samples/consul-ui/vendors/highlight/atom-one-dark.css +96 -0
- data/samples/consul-ui/vendors/highlight/highlight.pack.js +2 -0
- data/samples/criteo/vault-test.erb +6 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 875f33bec50159a51a9fb9e59b07fbe37a78a0587cb3a496cc42a1806b5c8c9d
|
4
|
+
data.tar.gz: 08f091608892658b875d716c6935c9fe24f19b72b0c37dcde07eee4bcbdea838
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 481afae4f8b4df204897584ebc0ad4559c54ea5cbdd4a693977c33a573acc67e3e726df8b55a6ed3d2506b01c82f3092d98d27208e0de51c1c2367df01447cb3
|
7
|
+
data.tar.gz: 361f0786469ff9c305f047824b42a8c801109ffe6c7283e0a4325c2f89c3d8dcfa989b3a52bbef982e8cc78bbad17505b2c5c2b57223b67ba0343c0c9722143a
|
data/CHANGELOG.md
CHANGED
@@ -2,8 +2,20 @@
|
|
2
2
|
|
3
3
|
## (UNRELEASED)
|
4
4
|
|
5
|
+
## 1.3.0 (June 7, 2018)
|
6
|
+
|
5
7
|
IMPROVEMENTS:
|
6
8
|
|
9
|
+
* samples/consul-ui/ now supports keys as well as nodes thans to [@geobeau](https://github.com/geobeau)
|
10
|
+
|
11
|
+
NEW FEATURES
|
12
|
+
|
13
|
+
* EXPERIMENTAL Vault support thanks to [@uepock](https://github.com/uepoch)
|
14
|
+
|
15
|
+
BUG FIXES:
|
16
|
+
|
17
|
+
* Properly shutdown connections when receiving Posix signal to kill app
|
18
|
+
|
7
19
|
## 1.2.1 (May 28, 2018)
|
8
20
|
|
9
21
|
BUG FIXES:
|
data/README.md
CHANGED
@@ -276,7 +276,7 @@ Please consult [CHANGELOG.md](CHANGELOG.md) for fixed bugs.
|
|
276
276
|
|
277
277
|
## TODO
|
278
278
|
|
279
|
-
* [
|
279
|
+
* [x] Hashi's Vault support (EXPERIMENTAL)
|
280
280
|
* [ ] Implement automatic dynamic rate limit
|
281
281
|
* [ ] More samples: apache, nginx, full website displaying consul information...
|
282
282
|
* [ ] Optimize rendering speed at start-up: an iteration is done very second by default, but it would be possible to speed
|
data/bin/consul-templaterb
CHANGED
@@ -17,6 +17,24 @@ def compute_default_output(source)
|
|
17
17
|
end
|
18
18
|
|
19
19
|
options = {
|
20
|
+
vault: {
|
21
|
+
debug: {
|
22
|
+
network: false
|
23
|
+
},
|
24
|
+
base_url: ENV['VAULT_ADDR'] || 'http://localhost:8200',
|
25
|
+
token: ENV['VAULT_TOKEN'] || nil,
|
26
|
+
token_renew: true,
|
27
|
+
retry_duration: 10,
|
28
|
+
lease_duration_factor: 0.5, # The time it waits before actualizing data based on the lease time: 2h lease * 0.5 = Fetch data every 1h
|
29
|
+
min_duration: 60,
|
30
|
+
max_retry_duration: 86_400, # Much higher than consul's because some dynamic secrets could be used for a day.
|
31
|
+
paths: {
|
32
|
+
'/v1/sys/mounts' => {
|
33
|
+
max_retry_duration: 600,
|
34
|
+
min_duration: 600
|
35
|
+
}
|
36
|
+
}
|
37
|
+
},
|
20
38
|
consul: {
|
21
39
|
debug: {
|
22
40
|
network: false
|
@@ -71,10 +89,27 @@ optparse = OptionParser.new do |opts|
|
|
71
89
|
options[:consul][:base_url] = consul_url
|
72
90
|
end
|
73
91
|
|
74
|
-
opts.on('
|
92
|
+
opts.on('--consul-token=<token>', String, 'Use a token to connect to Consul') do |consul_token|
|
75
93
|
options[:consul][:token] = consul_token
|
76
94
|
end
|
77
95
|
|
96
|
+
opts.on('-V', '--vault-addr=<address>', String, 'Address of Vault, eg: http://localhost:8200') do |vault_url|
|
97
|
+
options[:vault][:base_url] = vault_url
|
98
|
+
end
|
99
|
+
|
100
|
+
opts.on('-T', '--vault-token=<token>', String, 'Token used to authenticate against vault.') do |vault_token|
|
101
|
+
options[:vault][:token] = vault_token
|
102
|
+
end
|
103
|
+
|
104
|
+
options[:vault][:token_renew] = true
|
105
|
+
opts.on('--[no-]vault-renew', 'Control auto-renewal of the Vault token. Default: activated') do |vault_renew|
|
106
|
+
options[:vault][:token_renew] = vault_renew
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on('--vault-lease-duration-factor=<factor>', Float, 'Wait at least <factor> * lease time before updating a Vault secret. Default: 0.5') do |factor|
|
110
|
+
options[:vault][:lease_duration_factor] = factor
|
111
|
+
end
|
112
|
+
|
78
113
|
opts.on('-w', '--wait=<min_duration>', Float, 'Wait at least n seconds before each template generation') do |min_duration|
|
79
114
|
options[:consul][:min_duration] = min_duration
|
80
115
|
end
|
@@ -213,8 +248,10 @@ def find_max_descriptors(max_descripors)
|
|
213
248
|
max_size
|
214
249
|
end
|
215
250
|
|
216
|
-
|
217
|
-
options[
|
251
|
+
%i[consul vault].each do |type|
|
252
|
+
unless options[type][:base_url].start_with? 'http', 'https'
|
253
|
+
options[type][:base_url] = "http://#{options[type][:base_url]}"
|
254
|
+
end
|
218
255
|
end
|
219
256
|
|
220
257
|
# Since we might be using a lots of descriptors, document this
|
@@ -227,7 +264,8 @@ STDERR.puts "Max number of descriptors set to #{new_size}" if options[:consul][:
|
|
227
264
|
EM.epoll
|
228
265
|
|
229
266
|
consul_conf = Consul::Async::ConsulConfiguration.new(options[:consul])
|
230
|
-
|
267
|
+
vault_conf = Consul::Async::VaultConfiguration.new(options[:vault])
|
268
|
+
template_manager = Consul::Async::EndPointsManager.new(consul_conf, vault_conf)
|
231
269
|
|
232
270
|
ARGV.each do |tpl|
|
233
271
|
dest = compute_default_output(tpl)
|
@@ -239,6 +277,7 @@ end
|
|
239
277
|
%w[INT PIPE TERM].each do |sig|
|
240
278
|
Signal.trap(sig) do
|
241
279
|
STDERR.puts "[KILL] received #{sig}, stopping myself"
|
280
|
+
template_manager.terminate
|
242
281
|
kill_program
|
243
282
|
end
|
244
283
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'consul/async/utilities'
|
2
|
+
require 'consul/async/stats'
|
2
3
|
require 'em-http'
|
3
4
|
require 'thread'
|
4
5
|
require 'json'
|
@@ -56,38 +57,6 @@ module Consul
|
|
56
57
|
paths: @paths)
|
57
58
|
end
|
58
59
|
end
|
59
|
-
class ConsulEndPointStats
|
60
|
-
attr_reader :successes, :errors, :start, :body_bytes
|
61
|
-
def initialize
|
62
|
-
@start = Time.now.utc
|
63
|
-
@successes = 0
|
64
|
-
@errors = 0
|
65
|
-
@body_bytes = 0
|
66
|
-
end
|
67
|
-
|
68
|
-
def on_reponse(res)
|
69
|
-
@successes += 1
|
70
|
-
@body_bytes = body_bytes + res.http.response.bytesize
|
71
|
-
end
|
72
|
-
|
73
|
-
def on_error(_http)
|
74
|
-
@errors += 1
|
75
|
-
end
|
76
|
-
|
77
|
-
def bytes_per_sec
|
78
|
-
diff = (Time.now.utc - start)
|
79
|
-
diff = 1 if diff < 1
|
80
|
-
(body_bytes / diff).round(0)
|
81
|
-
end
|
82
|
-
|
83
|
-
def bytes_per_sec_human
|
84
|
-
"#{Utilities.bytes_to_h(bytes_per_sec)}/s"
|
85
|
-
end
|
86
|
-
|
87
|
-
def body_bytes_human
|
88
|
-
Utilities.bytes_to_h(body_bytes)
|
89
|
-
end
|
90
|
-
end
|
91
60
|
class ConsulResult
|
92
61
|
attr_reader :data, :http, :x_consul_index, :last_update, :stats, :retry_in
|
93
62
|
def initialize(data, modified, http, x_consul_index, stats, retry_in)
|
@@ -106,6 +75,7 @@ module Consul
|
|
106
75
|
|
107
76
|
def mutate(new_data)
|
108
77
|
@data = new_data.dup
|
78
|
+
@data_json = nil
|
109
79
|
end
|
110
80
|
|
111
81
|
def json
|
@@ -146,9 +116,9 @@ module Consul
|
|
146
116
|
@consecutive_errors = 0
|
147
117
|
@query_params = query_params
|
148
118
|
@stopping = false
|
149
|
-
@stats =
|
119
|
+
@stats = EndPointStats.new
|
150
120
|
@last_result = ConsulResult.new(default_value, false, HttpResponse.new(nil), 0, stats, 1)
|
151
|
-
on_response { |result| @stats.
|
121
|
+
on_response { |result| @stats.on_response result }
|
152
122
|
on_error { |http| @stats.on_error http }
|
153
123
|
_enable_network_debug if conf.debug && conf.debug[:network]
|
154
124
|
fetch
|
@@ -19,10 +19,11 @@ module Consul
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class
|
23
|
-
attr_reader :
|
24
|
-
def initialize(consul_configuration)
|
25
|
-
@
|
22
|
+
class EndPointsManager
|
23
|
+
attr_reader :consul_conf, :vault_conf, :net_info, :start_time
|
24
|
+
def initialize(consul_configuration, vault_configuration)
|
25
|
+
@consul_conf = consul_configuration
|
26
|
+
@vault_conf = vault_configuration
|
26
27
|
@endpoints = {}
|
27
28
|
@iteration = 1
|
28
29
|
@start_time = Time.now.utc
|
@@ -35,6 +36,11 @@ module Consul
|
|
35
36
|
current_erb_path: nil,
|
36
37
|
params: {}
|
37
38
|
}
|
39
|
+
|
40
|
+
unless @vault_conf.token.nil? || !@vault_conf.token_renew
|
41
|
+
#Setup token renewal
|
42
|
+
vault_setup_token_renew
|
43
|
+
end
|
38
44
|
end
|
39
45
|
|
40
46
|
# https://www.consul.io/api/health.html#list-nodes-for-service
|
@@ -45,7 +51,7 @@ module Consul
|
|
45
51
|
query_params[:dc] = dc if dc
|
46
52
|
query_params[:passing] = passing if passing
|
47
53
|
query_params[:tag] = tag if tag
|
48
|
-
create_if_missing(path, query_params) { ConsulTemplateService.new(ConsulEndpoint.new(
|
54
|
+
create_if_missing(path, query_params) { ConsulTemplateService.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
49
55
|
end
|
50
56
|
|
51
57
|
# https://www.consul.io/api/health.html#list-checks-for-service
|
@@ -55,7 +61,7 @@ module Consul
|
|
55
61
|
query_params = {}
|
56
62
|
query_params[:dc] = dc if dc
|
57
63
|
query_params[:passing] = passing if passing
|
58
|
-
create_if_missing(path, query_params) { ConsulTemplateChecks.new(ConsulEndpoint.new(
|
64
|
+
create_if_missing(path, query_params) { ConsulTemplateChecks.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
59
65
|
end
|
60
66
|
|
61
67
|
# https://www.consul.io/api/catalog.html#list-nodes
|
@@ -63,7 +69,7 @@ module Consul
|
|
63
69
|
path = '/v1/catalog/nodes'
|
64
70
|
query_params = {}
|
65
71
|
query_params[:dc] = dc if dc
|
66
|
-
create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(
|
72
|
+
create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
67
73
|
end
|
68
74
|
|
69
75
|
# https://www.consul.io/api/catalog.html#list-services-for-node
|
@@ -71,7 +77,7 @@ module Consul
|
|
71
77
|
path = "/v1/catalog/node/#{name_or_id}"
|
72
78
|
query_params = {}
|
73
79
|
query_params[:dc] = dc if dc
|
74
|
-
create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(
|
80
|
+
create_if_missing(path, query_params) { ConsulTemplateNodes.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
|
75
81
|
end
|
76
82
|
|
77
83
|
# https://www.consul.io/api/agent.html#read-configuration
|
@@ -79,7 +85,7 @@ module Consul
|
|
79
85
|
path = '/v1/agent/self'
|
80
86
|
query_params = {}
|
81
87
|
default_value = '{"Config":{}, "Coord":{}, "Member":{}, "Meta":{}, "Stats":{}}'
|
82
|
-
create_if_missing(path, query_params) { ConsulAgentSelf.new(ConsulEndpoint.new(
|
88
|
+
create_if_missing(path, query_params) { ConsulAgentSelf.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
|
83
89
|
end
|
84
90
|
|
85
91
|
# https://www.consul.io/api/agent.html#view-metrics
|
@@ -87,7 +93,7 @@ module Consul
|
|
87
93
|
path = '/v1/agent/metrics'
|
88
94
|
query_params = {}
|
89
95
|
default_value = '{"Gauges":[], "Points":[], "Member":{}, "Counters":[], "Samples":{}}'
|
90
|
-
create_if_missing(path, query_params) { ConsulAgentMetrics.new(ConsulEndpoint.new(
|
96
|
+
create_if_missing(path, query_params) { ConsulAgentMetrics.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value)) }
|
91
97
|
end
|
92
98
|
|
93
99
|
# Return a param of template
|
@@ -105,14 +111,14 @@ module Consul
|
|
105
111
|
query_params[:dc] = dc if dc
|
106
112
|
# Tag filtering is performed on client side
|
107
113
|
query_params[:tag] = tag if tag
|
108
|
-
create_if_missing(path, query_params) { ConsulTemplateServices.new(ConsulEndpoint.new(
|
114
|
+
create_if_missing(path, query_params) { ConsulTemplateServices.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '{}')) }
|
109
115
|
end
|
110
116
|
|
111
117
|
# https://www.consul.io/api/catalog.html#list-datacenters
|
112
118
|
def datacenters
|
113
119
|
path = '/v1/catalog/datacenters'
|
114
120
|
query_params = {}
|
115
|
-
create_if_missing(path, query_params) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(
|
121
|
+
create_if_missing(path, query_params) { ConsulTemplateDatacenters.new(ConsulEndpoint.new(consul_conf, path, true, query_params, '[]')) }
|
116
122
|
end
|
117
123
|
|
118
124
|
# https://www.consul.io/api/kv.html#read-key
|
@@ -123,7 +129,23 @@ module Consul
|
|
123
129
|
query_params[:recurse] = recurse if recurse
|
124
130
|
query_params[:keys] = keys if keys
|
125
131
|
default_value = '[]'
|
126
|
-
create_if_missing(path, query_params) { ConsulTemplateKV.new(ConsulEndpoint.new(
|
132
|
+
create_if_missing(path, query_params) { ConsulTemplateKV.new(ConsulEndpoint.new(consul_conf, path, true, query_params, default_value), name) }
|
133
|
+
end
|
134
|
+
|
135
|
+
def secrets(path = '')
|
136
|
+
raise "You need to provide a vault token to use 'secret' keyword" if vault_conf.token.nil?
|
137
|
+
path = "/v1/#{path}".gsub(/\/{2,}/, '/')
|
138
|
+
query_params = {list: "true"}
|
139
|
+
create_if_missing(path, query_params) { ConsulTemplateVaultSecretList.new(VaultEndpoint.new(vault_conf, path, 'GET',true, query_params,JSON.generate(data: {keys: []}))) }
|
140
|
+
end
|
141
|
+
|
142
|
+
def secret(path = '', post_data = nil )
|
143
|
+
puts post_data
|
144
|
+
raise "You need to provide a vault token to use 'secrets' keyword" if vault_conf.token.nil?
|
145
|
+
path = "/v1/#{path}"
|
146
|
+
query_params = {}
|
147
|
+
method = post_data ? "POST" : "GET"
|
148
|
+
create_if_missing(path, query_params) { ConsulTemplateVaultSecret.new(VaultEndpoint.new(vault_conf, path, method, true, query_params, JSON.generate(data: {}))) }
|
127
149
|
end
|
128
150
|
|
129
151
|
# render a relative file with the given params accessible from template
|
@@ -204,6 +226,12 @@ module Consul
|
|
204
226
|
@endpoints = {}
|
205
227
|
end
|
206
228
|
|
229
|
+
def vault_setup_token_renew
|
230
|
+
path = 'v1/auth/token/renew-self'
|
231
|
+
STDERR.print "[INFO] Setting up vault token renewal"
|
232
|
+
VaultEndpoint.new(vault_conf, path, :POST, {}, {})
|
233
|
+
end
|
234
|
+
|
207
235
|
def create_if_missing(path, query_params)
|
208
236
|
fqdn = path.dup
|
209
237
|
query_params.each_pair do |k, v|
|
@@ -384,5 +412,18 @@ module Consul
|
|
384
412
|
end
|
385
413
|
end
|
386
414
|
end
|
415
|
+
|
416
|
+
class ConsulTemplateVaultSecret < ConsulTemplateAbstractMap
|
417
|
+
def initialize(vault_endpoint)
|
418
|
+
super(vault_endpoint)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
class ConsulTemplateVaultSecretList < ConsulTemplateAbstractArray
|
422
|
+
def parse_result(res)
|
423
|
+
return res if res.data.nil?
|
424
|
+
res.mutate(JSON.generate(res.json['data']['keys']))
|
425
|
+
res
|
426
|
+
end
|
427
|
+
end
|
387
428
|
end
|
388
429
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'consul/async/utilities'
|
2
2
|
require 'consul/async/consul_endpoint'
|
3
|
+
require 'consul/async/vault_endpoint'
|
3
4
|
require 'consul/async/consul_template'
|
4
5
|
require 'consul/async/consul_template_render'
|
5
6
|
require 'em-http'
|
@@ -55,7 +56,7 @@ module Consul
|
|
55
56
|
template_manager.terminate
|
56
57
|
EventMachine.stop
|
57
58
|
rescue StandardError => e
|
58
|
-
STDERR.puts "[ERROR] Fatal error occured: #{e.inspect} - #{e.backtrace}"
|
59
|
+
STDERR.puts "[ERROR] Fatal error occured: #{e.inspect} - #{e.backtrace.join("\n\t")}"
|
59
60
|
template_manager.terminate
|
60
61
|
EventMachine.stop
|
61
62
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'consul/async/utilities'
|
2
|
+
|
3
|
+
class Endpoint
|
4
|
+
attr_reader :conf, :path, :x_consul_index, :queue, :stats, :last_result, :enforce_json_200, :start_time, :default_value, :query_params
|
5
|
+
def initialize(conf, path, enforce_json_200 = true, query_params = {}, default_value = '[]')
|
6
|
+
@conf = conf.create(path)
|
7
|
+
@default_value = default_value
|
8
|
+
@path = path
|
9
|
+
@queue = EM::Queue.new
|
10
|
+
@s_callbacks = []
|
11
|
+
@e_callbacks = []
|
12
|
+
@enforce_json_200 = enforce_json_200
|
13
|
+
@start_time = Time.now.utc
|
14
|
+
@consecutive_errors = 0
|
15
|
+
@query_params = query_params
|
16
|
+
@stopping = false
|
17
|
+
@stats = EndPointStats.new
|
18
|
+
@last_result = ConsulResult.new(default_value, false, HttpResponse.new(nil), 0, stats, 1)
|
19
|
+
on_response { |result| @stats.on_reponse result }
|
20
|
+
on_error { |http| @stats.on_error http }
|
21
|
+
_enable_network_debug if conf.debug && conf.debug[:network]
|
22
|
+
fetch
|
23
|
+
queue << 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def _enable_network_debug
|
27
|
+
on_response do |result|
|
28
|
+
state = result.x_consul_index.to_i < 1 ? '[WARN]' : '[ OK ]'
|
29
|
+
stats = result.stats
|
30
|
+
STDERR.puts "[DEBUG]#{state}#{result.modified? ? '[MODIFIED]' : '[NO DIFF]'}" \
|
31
|
+
"[s:#{stats.successes},err:#{stats.errors}]" \
|
32
|
+
"[#{stats.body_bytes_human.ljust(8)}][#{stats.bytes_per_sec_human.ljust(9)}]"\
|
33
|
+
" #{path.ljust(48)} idx:#{result.x_consul_index}, next in #{result.retry_in} s"
|
34
|
+
end
|
35
|
+
on_error { |http| STDERR.puts "[ERROR]: #{path}: #{http.error}" }
|
36
|
+
end
|
37
|
+
|
38
|
+
def on_response(&block)
|
39
|
+
@s_callbacks << block
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_error(&block)
|
43
|
+
@e_callbacks << block
|
44
|
+
end
|
45
|
+
|
46
|
+
def ready?
|
47
|
+
@ready
|
48
|
+
end
|
49
|
+
|
50
|
+
def terminate
|
51
|
+
@stopping = true
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def build_request(headers = {}, query_params = {})
|
57
|
+
req = {
|
58
|
+
head: headers,
|
59
|
+
path: path,
|
60
|
+
query: query_params,
|
61
|
+
keepalive: true,
|
62
|
+
callback: method(:on_response)
|
63
|
+
}
|
64
|
+
@query_params.each_pair do |k, v|
|
65
|
+
req[:query][k] = v
|
66
|
+
end
|
67
|
+
req
|
68
|
+
end
|
69
|
+
|
70
|
+
def log(level, message)
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
def _handle_error(http, consul_index)
|
75
|
+
retry_in = [600, conf.retry_duration + 2**@consecutive_errors].min
|
76
|
+
STDERR.puts "[ERROR][#{path}] X-Consul-Index:#{consul_index} - #{http.error} - Retry in #{retry_in}s #{stats.body_bytes_human}"
|
77
|
+
@consecutive_errors += 1
|
78
|
+
http_result = HttpResponse.new(http)
|
79
|
+
EventMachine.add_timer(retry_in) do
|
80
|
+
yield
|
81
|
+
queue.push(consul_index)
|
82
|
+
end
|
83
|
+
@e_callbacks.each { |c| c.call(http_result) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def fetch
|
87
|
+
options = {
|
88
|
+
connect_timeout: 5, # default connection setup timeout
|
89
|
+
inactivity_timeout: conf.wait_duration + 1, # default connection inactivity (post-setup) timeout
|
90
|
+
}
|
91
|
+
connection = EventMachine::HttpRequest.new(conf.base_url, options)
|
92
|
+
cb = proc do |consul_index|
|
93
|
+
http = connection.get(build_request(consul_index))
|
94
|
+
http.callback do
|
95
|
+
# Dirty hack, but contrary to other path, when key is not present, Consul returns 404
|
96
|
+
is_kv_empty = path.start_with?('/v1/kv') && http.response_header.status == 404
|
97
|
+
if !is_kv_empty && enforce_json_200 && http.response_header.status != 200 && http.response_header['Content-Type'] != 'application/json'
|
98
|
+
_handle_error(http, consul_index) { connection = EventMachine::HttpRequest.new(conf.base_url, options) }
|
99
|
+
else
|
100
|
+
n_consul_index = find_x_consul_token(http)
|
101
|
+
@consecutive_errors = 0
|
102
|
+
http_result = if is_kv_empty
|
103
|
+
HttpResponse.new(http, default_value)
|
104
|
+
else
|
105
|
+
HttpResponse.new(http)
|
106
|
+
end
|
107
|
+
new_content = http_result.response.freeze
|
108
|
+
modified = @last_result.nil? ? true : @last_result.data != new_content
|
109
|
+
if n_consul_index == consul_index || n_consul_index.nil?
|
110
|
+
retry_in = modified ? conf.missing_index_retry_time_on_diff : conf.missing_index_retry_time_on_unchanged
|
111
|
+
n_consul_index = consul_index
|
112
|
+
else
|
113
|
+
retry_in = modified ? conf.min_duration : conf.retry_on_non_diff
|
114
|
+
end
|
115
|
+
retry_in = 0.1 if retry_in < 0.1
|
116
|
+
unless @stopping
|
117
|
+
EventMachine.add_timer(retry_in) do
|
118
|
+
queue.push(n_consul_index)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
result = ConsulResult.new(new_content, modified, http_result, n_consul_index, stats, retry_in)
|
122
|
+
@last_result = result
|
123
|
+
@ready = true
|
124
|
+
@s_callbacks.each { |c| c.call(result) }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
http.errback do
|
129
|
+
unless @stopping
|
130
|
+
_handle_error(http, consul_index) { connection = EventMachine::HttpRequest.new(conf.base_url, options) }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
queue.pop(&cb)
|
134
|
+
end
|
135
|
+
queue.pop(&cb)
|
136
|
+
end
|
137
|
+
end
|