consul-templaterb 1.25.2 → 1.27.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)
@@ -46,10 +53,15 @@ module Consul
46
53
  end
47
54
  end
48
55
 
49
- def create(path)
56
+ def create(path, agent: nil)
50
57
  return self unless @paths[path.to_sym]
51
58
 
52
- VaultConfiguration.new(base_url: ch(path, :base_url),
59
+ base_url = ch(path, :base_url)
60
+ if agent
61
+ agent = "http://#{agent}" unless agent.start_with? 'http', 'https'
62
+ base_url = agent
63
+ end
64
+ VaultConfiguration.new(base_url: base_url,
53
65
  debug: ch(path, :debug),
54
66
  token: ch(path, :token),
55
67
  retry_duration: ch(path, :retry_duration),
@@ -117,8 +129,8 @@ module Consul
117
129
  class VaultEndpoint
118
130
  attr_reader :conf, :path, :http_method, :queue, :stats, :last_result, :enforce_json_200, :start_time, :default_value, :query_params
119
131
 
120
- def initialize(conf, path, http_method = 'GET', enforce_json_200 = true, query_params = {}, default_value = '{}', post_data = {})
121
- @conf = conf.create(path)
132
+ def initialize(conf, path, http_method = 'GET', enforce_json_200 = true, query_params = {}, default_value = '{}', post_data = {}, agent: nil)
133
+ @conf = conf.create(path, agent: agent)
122
134
  @default_value = default_value
123
135
  @path = path
124
136
  @http_method = http_method
@@ -221,6 +233,13 @@ module Consul
221
233
  connect_timeout: 5, # default connection setup timeout
222
234
  inactivity_timeout: 1 # default connection inactivity (post-setup) timeout
223
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
224
243
  connection = EventMachine::HttpRequest.new(conf.base_url, options)
225
244
  cb = proc do |_|
226
245
  http = connection.send(http_method.downcase, build_request) # Under the hood: c.send('get', {stuff}) === c.get({stuff})
@@ -1,5 +1,5 @@
1
1
  module Consul
2
2
  module Async
3
- VERSION = '1.25.2'.freeze
3
+ VERSION = '1.27.0'.freeze
4
4
  end
5
5
  end
@@ -9,7 +9,7 @@
9
9
  require 'set'
10
10
 
11
11
  # Services to hide
12
- services_blacklist_raw = (ENV['EXCLUDE_SERVICES'] || '.*netsvc-probe.*,.*consul-probed.*').split(',')
12
+ services_blacklist_raw = (ENV['EXCLUDE_SERVICES'] || '.*netsvc-probe.*,.*consul-probed.*,.lbl7.*').split(',')
13
13
  services_blacklist = services_blacklist_raw.map { |v| Regexp.new(v) }
14
14
 
15
15
  num_services={}
@@ -0,0 +1,69 @@
1
+ __________________________________________________________________
2
+ | DC | Services |______________Instances________________| Nodes |
3
+ | | | Passing | Warning | Critic | Total | |
4
+ |-------+----------+---------+---------+---------+---------+-------|
5
+ <%
6
+ # This template list all instances on all DCs
7
+ # And aggregates a list of Services, Services Instances/status
8
+ # And nodes.
9
+ require 'set'
10
+
11
+
12
+ def find_best_agent_for_dc(dc)
13
+ agent = service('consul-relay', passing: true, dc:dc).first
14
+ agent = service('consul-agent-http', passing: true, dc:dc).first unless agent
15
+ service('consul', passing: true, dc:dc).first unless agent
16
+ return nil unless agent
17
+ port = agent['Service']['Port'] || 8500
18
+ "http://#{agent.service_address}:#{port}"
19
+ end
20
+
21
+ # Services to hide
22
+ services_blacklist_raw = (ENV['EXCLUDE_SERVICES'] || '.*netsvc-probe.*,.*consul-probed.*').split(',')
23
+ services_blacklist = services_blacklist_raw.map { |v| Regexp.new(v) }
24
+
25
+ num_services={}
26
+ num_instances={}
27
+ all_states = ['passing', 'warning', 'critical', 'total']
28
+ distinct_services=Set.new
29
+ datacenters().each do |dc|
30
+ next unless find_best_agent_for_dc(dc)
31
+ num_services[dc] = 0
32
+ num_instances[dc] = {
33
+ 'passing' => 0,
34
+ 'warning' => 0,
35
+ 'critical' => 0,
36
+ 'total' => 0
37
+ }
38
+ services(dc:dc, agent: find_best_agent_for_dc(dc)).each do |service_name, tags|
39
+ next if services_blacklist.any? {|r| r.match(service_name)}
40
+ distinct_services.add(service_name)
41
+ num_services[dc]+=1
42
+ service(service_name, dc:dc, agent: find_best_agent_for_dc(dc)).each do |snode|
43
+ num_instances[dc][snode.status]+=1
44
+ num_instances[dc]['total']+=1
45
+ end
46
+ end
47
+ end
48
+ num_instances_total={
49
+ 'passing' => 0,
50
+ 'warning' => 0,
51
+ 'critical' => 0,
52
+ 'total' => 0
53
+ }
54
+ num_nodes_total=0
55
+ num_services.each do |dc, num_services_for_dc|
56
+ all_states.each do |s|
57
+ num_instances_total[s] += num_instances[dc][s]
58
+ end
59
+ num_nodes=nodes(dc:dc, agent: find_best_agent_for_dc(dc)).count
60
+ num_nodes_total+=num_nodes
61
+ %>| <%= dc.rjust(5) %> | <%= num_services_for_dc.to_s.rjust(8) %> |<% all_states.each do |status|
62
+ %> <%= num_instances[dc][status].to_s.rjust(7) %> |<% end %> <%= num_nodes.to_s.rjust(5) %> |
63
+ <%
64
+ end
65
+ %>|-------+----------+---------+---------+---------+---------+-------|
66
+ | TOTAL | <%= distinct_services.count.to_s.rjust(8) %> |<% all_states.each do |status|
67
+ %> <%= num_instances_total[status].to_s.rjust(7) %> |<%
68
+ end %> <%= num_nodes_total.to_s.rjust(5) %> |
69
+ '_______|__________|_________|_________|_________|_________|_______'
@@ -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
+ %>
@@ -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
- // Utilities
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
- const data_date = await httpResponse.headers.get('X-Consul-Snapshot-Timestamp');
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
+ }