consul-templaterb 1.8.8 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c516359f510134a8471b94ba58a0a762d38b45cd7058d3b8d45ff67837047139
4
- data.tar.gz: b24b6f18c7d33561bb82b9df4ca6422052762e08b6ffb21fc76b82b55659cf3d
3
+ metadata.gz: 8efd0da9a415f6e58d2da8e712ea082efd08b51631b38c31db287cbbdf3a3bbe
4
+ data.tar.gz: 9e1fdd39625a7213dfb431dc89660fdeb0da382c4459766f5a2d34803313b9fd
5
5
  SHA512:
6
- metadata.gz: 281fa6348ebac8e6b3c143979a9b21dd182b43d3933ac6ed6f177cb4e3ab86f1db290406de8150abf77c580e17cbaaadac01673efa169c675a83e426fab165ab
7
- data.tar.gz: 86421aa3c59e9fc1073c46346cf61dde419ed389f40b549643d1f078723d3a587a4735d6c9882c44e33bd0d0958f20314572d5ead61988f0c9ac9c53b6b88b29
6
+ metadata.gz: 5f7c6e9187971dda5aadab619fe58facf7c76fe6cf7dde9a5d9c3708d525e8a3fc4bd8b8b558ccfcd8a3b2fafaf923acb7f5f673e9428635ef9b391ec50f6f79
7
+ data.tar.gz: 8031be2c311b9e1f12127a5e15922d13a12f86c86e2e0ec1ea92c1282c7f382fdab2f851c6bbb43b6d63dba91a8eb731df7825d93aaae7164407db1b1c48ebcf
data/.rubocop.yml CHANGED
@@ -16,7 +16,7 @@ Metrics/ClassLength:
16
16
  Max: 225
17
17
 
18
18
  Metrics/CyclomaticComplexity:
19
- Max: 15
19
+ Max: 20
20
20
 
21
21
  Metrics/LineLength:
22
22
  Max: 175
@@ -28,7 +28,7 @@ Metrics/ParameterLists:
28
28
  Max: 12
29
29
 
30
30
  Metrics/PerceivedComplexity:
31
- Max: 18
31
+ Max: 20
32
32
 
33
33
  Style/Documentation:
34
34
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  ## (UNRELEASED)
4
4
 
5
+ ## 1.9.0
6
+
7
+ OPTIMIZATIONS:
8
+ * Better network issue because of X-Consul-Index parsing bug
9
+
10
+ NEW FEATURES:
11
+ * value.endpoint.x_consul_index now works as expected
12
+
13
+ IMPROVEMENTS:
14
+ * timeline now works well behing load balancer without stickyness
15
+ * Now hide service column when filtering on a service
16
+ * apply row limits immediately
17
+ * auto-refresh duration is now configurable by `TIMELINE_REFRESH_MS`
18
+ environment variable
19
+
5
20
  ## 1.8.6 / 1.8.7 / 1.8.8 (December 20, 2018)
6
21
 
7
22
  OPTIMIZATIONS:
@@ -181,8 +181,8 @@ module Consul
181
181
  res
182
182
  end
183
183
 
184
- def find_x_consul_token(http)
185
- http.response_header['X_CONSUL_INDEX']
184
+ def find_x_consul_index(http)
185
+ http.response_header['X-Consul-Index']
186
186
  end
187
187
 
188
188
  def _handle_error(http, consul_index)
@@ -211,7 +211,8 @@ module Consul
211
211
  if !is_kv_empty && enforce_json_200 && http.response_header.status != 200 && http.response_header['Content-Type'] != 'application/json'
212
212
  _handle_error(http, consul_index) { connection = EventMachine::HttpRequest.new(conf.base_url, options) }
213
213
  else
214
- n_consul_index = find_x_consul_token(http)
214
+ n_consul_index = find_x_consul_index(http)
215
+ @x_consul_index = n_consul_index.to_i if n_consul_index
215
216
  @consecutive_errors = 0
216
217
  http_result = if is_kv_empty
217
218
  HttpResponse.new(http, default_value)
@@ -1,5 +1,5 @@
1
1
  module Consul
2
2
  module Async
3
- VERSION = '1.8.8'.freeze
3
+ VERSION = '1.9.0'.freeze
4
4
  end
5
5
  end
@@ -1,6 +1,6 @@
1
1
  <% datasource = ENV['TIMELINE_DATASOURCE'] || 'timeline.json'
2
2
  # Time to wait before reloading configuration again in seconds (0 = never)
3
- refresh = ENV['REFRESH'] || '3600' %><%= render_file('common/header.html.erb', title: 'Services Timeline') %>
3
+ refresh = ENV['TIMELINE_REFRESH_MS'] || '10000' %><%= render_file('common/header.html.erb', title: 'Services Timeline') %>
4
4
  <div class="main">
5
5
  <div class="row mx-0">
6
6
  <div id="filter-menu" class="col-2 col-m-3 px-4 pt-4">
@@ -27,7 +27,10 @@
27
27
  <span id="numRowsDisplayed" class="lookup badge badge-pill badge-primary">loading&hellip;</span>
28
28
  </span>
29
29
  </div>
30
- <input id="maxRows" title="max rows to display" placeholder="max rows" style="width: 5em !important;" type="number" min="100" max="<%= (ENV['CONSUL_TIMELINE_BUFFER'] || 10000) %>" step="25" value="5000" class="form-control"/>
30
+ <button class="btn btn-primary btn-sm" id="firstPage" title="Newest entries" disabled="disabled" onclick="serviceTimeline.firstPage()">⇤</button>
31
+ <button class="btn btn-primary btn-sm" id="prevPage" title="Newer entries" disabled="disabled" onclick="serviceTimeline.prevPage()">←</button>
32
+ <input id="maxRows" title="max rows to display" placeholder="max rows" style="width: 5.5em" type="number" min="25" max="<%= (ENV['CONSUL_TIMELINE_BUFFER'] || 10000) %>" step="25" value="250" class="form-control" onchange="serviceTimeline.displayEvents(false)"/>
33
+ <button class="btn btn-primary btn-sm" id="nextPage" disabled="disabled" onclick="serviceTimeline.nextPage()">→</button>
31
34
  </div>
32
35
  </div>
33
36
  </div>
@@ -22,6 +22,7 @@
22
22
  }
23
23
  .checkName {
24
24
  padding-left: 5px;
25
+ max-width: 12em;
25
26
  overflow: hidden;
26
27
  white-space: nowrap;
27
28
  text-overflow: ellipsis;
@@ -93,11 +94,11 @@ td.serviceName {
93
94
  border-left: 0px;
94
95
  border-right: 0px;
95
96
  }
96
- .service-status {
97
- width: 10em;
97
+ td.evt-service-status {
98
+ min-width: 10em;
98
99
  }
99
- .all-instances {
100
- width: 14em;
100
+ td.all-instances {
101
+ min-width: 10em;
101
102
  text-align: right;
102
103
  }
103
104
  #node-statuses span {
@@ -221,4 +222,4 @@ pre, code {
221
222
  }
222
223
  .connect-disabled{
223
224
  display: none;
224
- }
225
+ }
@@ -17,13 +17,17 @@ class ServiceTimeline {
17
17
  this.lastEntryLoaded = null;
18
18
  this.presentServices = {}
19
19
  this.currentlyUpdating = false;
20
+ this.start = null;
21
+ this.next = null;
22
+ this.prev = null;
20
23
  var sT = this;
21
24
  }
22
25
 
23
26
  fetchRessource(firstReload) {
24
- $.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this,
27
+ $.ajax({url: this.ressourceURL, cache: true, dataType: "json", sourceObject: this,
25
28
  success: function(result){
26
29
  serviceTimeline.initRessource(result, firstReload);
30
+ serviceTimeline.currentlyUpdating = false;
27
31
  },
28
32
  error: function(err) {
29
33
  console.log("Error updating data", err);
@@ -32,7 +36,69 @@ class ServiceTimeline {
32
36
  });
33
37
  }
34
38
 
39
+ nextPage() {
40
+ this.start = this.next;
41
+ console.log("nextPage() at ", this.start);
42
+ this.doFilter();
43
+ }
44
+
45
+ prevPage() {
46
+ this.start = this.prev;
47
+ console.log("prevPage() at ", this.start);
48
+ this.doFilter();
49
+ }
50
+
51
+ firstPage() {
52
+ this.start = null;
53
+ this.doFilter();
54
+ }
55
+
35
56
  initRessource(data, firstReload) {
57
+ if (!data || data.length < 1) {
58
+ console.log("No new data to load ", data);
59
+ return;
60
+ }
61
+ if (this.data != null) {
62
+ var previousCount = this.data.length;
63
+ if (previousCount > 0) {
64
+ var previousMaxIndex = indexOfTimelineEvent(this.data[previousCount - 1]);
65
+ var newIndex = indexOfTimelineEvent(data[data.length - 1]);
66
+ if (newIndex < previousMaxIndex) {
67
+ console.log("New index " + newIndex + " is lower than previous one", previousMaxIndex, " no need to reload");
68
+ return;
69
+ } else if (newIndex == previousMaxIndex) {
70
+ if (this.data.length >= data.length) {
71
+ //console.log("new := (idx=", newIndex, ",len=", data.length, ") old := (idx:=", previousMaxIndex, this.data.length, "), no need to reload");
72
+ return;
73
+ }
74
+ } else {
75
+ // Lets merge old data with new one
76
+ if (this.data.length > data.length) {
77
+ var diff = this.data.length - data.length;
78
+ var firstNewItem = indexOfTimelineEvent(data[0]);
79
+ var maxItemsInMemory = 50000;
80
+ if (maxItemsInMemory > data.length) {
81
+ console.log("Looking for matches with ", firstNewItem);
82
+ for (var i = 0 ; i < this.data.length; i++){
83
+ var oldVal = indexOfTimelineEvent(this.data[i]);
84
+ if (oldVal == firstNewItem) {
85
+ // We found the common start
86
+ var maxItemsToAdd = maxItemsInMemory - data.length;
87
+ var start = 0;
88
+ if (i > maxItemsToAdd) {
89
+ start = i - maxItemsToAdd;
90
+ }
91
+ console.log("Did preprend ", (i - start), "items to data");
92
+ data = this.data.slice(start, i).concat(data);
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ console.log("Loading", data.length, "items...");
36
102
  this.data = data;
37
103
  this.reloadTimeline(firstReload);
38
104
  }
@@ -132,7 +198,8 @@ class ServiceTimeline {
132
198
  if (!found) {
133
199
  sT.selectService($('#anyService', false));
134
200
  }
135
- setInterval(sT.reloadDataFromJSON, 10000);
201
+ console.log("Starting autorefresh every ", serviceTimeline.refresh, "ms.")
202
+ setInterval(sT.reloadDataFromJSON, serviceTimeline.refresh);
136
203
  }, 150);
137
204
  }
138
205
  }
@@ -232,13 +299,16 @@ class ServiceTimeline {
232
299
  var filterValue = $('#instance-filter')[0].value;
233
300
  var serviceName = serviceTimeline.serviceInstanceFilter;
234
301
  var serviceEvaluator = function(){return true};
302
+ var stylesheetText = '';
235
303
  if (serviceName != '' && serviceName != 'All') {
236
- serviceEvaluator = function(e){ return e.service === serviceName }
304
+ stylesheetText = '.serviceCol { display: none; }';
305
+ serviceEvaluator = function(e){ return e.service === serviceName }
237
306
  }
307
+ // Show / hide .serviceCol content
308
+ document.getElementById('serviceCol').textContent = stylesheetText;
238
309
  var maxRows = document.getElementById("maxRows").value;
239
310
  //$("#service-title").html(service['name']);
240
311
  var tableBody = $('#all-events > tbody');
241
- var startIndex = 0;
242
312
  var newDoc = document.createDocumentFragment();
243
313
  var frag = document.createElement('tbody');
244
314
  newDoc.appendChild(frag);
@@ -289,12 +359,56 @@ class ServiceTimeline {
289
359
  return false;
290
360
  }
291
361
  }
292
- for (var i = this.data.length - 1 ; i >= 0 && count <= maxRows; i--) {
362
+ var startIdx = this.start;
363
+ if (startIdx == null) {
364
+ startIdx = (this.data[this.data.length - 1].idx);
365
+ } else {
366
+ //console.log("startIdx := ", startIdx);
367
+ }
368
+ var previousElements = [];
369
+ document.getElementById('prevPage').setAttribute('disabled', 'disabled');
370
+ this.next = null;
371
+ document.getElementById('firstPage').setAttribute('disabled', 'disabled');
372
+ this.prev = null;
373
+ document.getElementById('nextPage').setAttribute('disabled', 'disabled');
374
+ var totalSkipped = 0;
375
+ for (var i = this.data.length - 1 ; i >= 0; i--) {
376
+ if (count >= maxRows) {
377
+ this.next = this.data[i].idx;
378
+ //console.log("next is ", this.data[i].ts, ' at ', this.next);
379
+ var nextTs = this.data[i].ts;
380
+ setTimeout(function(){
381
+ nextPage = document.getElementById('nextPage');
382
+ nextPage.removeAttribute('disabled');
383
+ nextPage.setAttribute('title', 'Older entries at ' + nextTs);
384
+ }, 1);
385
+ break;
386
+ }
293
387
  var e = this.data[i];
294
388
  if (!serviceEvaluator(e)) {
295
389
  continue;
296
390
  }
391
+ if (startIdx < e.idx) {
392
+ totalSkipped++;
393
+ previousElements.push(e);
394
+ var previousCounter = previousElements.length;
395
+ if (previousCounter == 1) {
396
+ setTimeout(function(){
397
+ document.getElementById('prevPage').removeAttribute('disabled');
398
+ document.getElementById('firstPage').removeAttribute('disabled');
399
+ }, 1);
400
+ } else if (previousCounter >= maxRows){
401
+ previousElements.shift();
402
+ }
403
+ continue;
404
+ }
297
405
  count++;
406
+ if (count == 1) {
407
+ if (previousElements.length > 0) {
408
+ //console.log("Current is ", e.ts, ' aka ', e.idx, ", Had ", previousElements.length, 'elements before, skipped := ', totalSkipped, "skipped elements", previousElements);
409
+ this.prev = previousElements.shift().idx;
410
+ }
411
+ }
298
412
  var row = document.createElement('tr');
299
413
  row.setAttribute("class", 'srv-' + e.service);
300
414
  var timestamp;
@@ -312,7 +426,7 @@ class ServiceTimeline {
312
426
  {
313
427
  var timeElement = this.appChild('time', document.createTextNode(timestamp));
314
428
  timeElement.setAttribute('datetime', fullTimestamp);
315
- timeElement.setAttribute('title', fullTimestamp);
429
+ timeElement.setAttribute('title', fullTimestamp + '\nX-Consul-Index: ' +e.idx);
316
430
  this.buildCell(row, 'td', 'ts', timeElement);
317
431
  }
318
432
  this.buildCell(row, 'td', 'lookup serviceName serviceCol', document.createTextNode(e.service));
@@ -352,10 +466,10 @@ class ServiceTimeline {
352
466
  }
353
467
  {
354
468
  if (e.new_state == e.old_state) {
355
- var td = this.buildCell(row, 'td', 'service-status', this.createBadge(e['new_state']));
469
+ var td = this.buildCell(row, 'td', 'evt-service-status', this.createBadge(e['new_state']));
356
470
  } else {
357
471
  var old = e.old_state == null ? this.createBadge('new', 'primary') : this.createBadge(e.old_state);
358
- var td = this.buildCell(row, 'td', 'service-status', old);
472
+ var td = this.buildCell(row, 'td', 'evt-service-status', old);
359
473
  td.appendChild(document.createTextNode('→'));
360
474
  var newState = (e.new_state == null) ? this.createBadge('removed', 'dark') : this.createBadge(e.new_state);
361
475
  td.appendChild(newState);
@@ -375,25 +489,28 @@ class ServiceTimeline {
375
489
  allInstances.appendChild(elem);
376
490
  }
377
491
  var elem = this.createBadge(nCritical);
378
- var percent = Math.round(nSuccess * 100 / e['stats']['total']);
379
- var clazz = 'secondary'
380
- if (percent > 90) {
381
- clazz = 'success';
382
- } else if (percent < 20) {
383
- clazz = 'danger';
384
- } else if (percent < 50) {
385
- clazz = 'warning';
492
+ if (e['stats']['total'] > 0) {
493
+ var percent = Math.round(nSuccess * 100 / e['stats']['total']);
494
+ var clazz = 'secondary'
495
+ if (percent > 90) {
496
+ clazz = 'success';
497
+ } else if (percent < 20) {
498
+ clazz = 'danger';
499
+ } else if (percent < 50) {
500
+ clazz = 'warning';
501
+ }
502
+ this.buildCell(row, 'td', 'ipercents', this.createBadge(percent + " %", clazz));
503
+ } else {
504
+ this.buildCell(row, 'td', 'ipercents', this.createBadge('none', 'danger'));
386
505
  }
387
- this.buildCell(row, 'td', 'ipercents', this.createBadge(percent + " %", clazz));
388
506
  }
389
507
  frag.append(row);
390
508
  }
391
- $('#numRowsDisplayed').html(count + ' / ' + this.data.length);
509
+ $('#numRowsDisplayed').html(totalSkipped + '…' + (totalSkipped + count) + ' / ' + this.data.length);
392
510
  var tbody = tableBody[0];
393
511
  tbody.parentNode.replaceChild(frag, tbody);
394
512
  if (this.data.length > 1) {
395
513
  this.lastEntryLoaded = this.data[this.data.length - 1];
396
- console.log("Last entry loaded: ", indexOfTimelineEvent(this.lastEntryLoaded));
397
514
  }
398
515
  }
399
- }
516
+ }
@@ -4,6 +4,8 @@ require 'set'
4
4
  services_blacklist = (ENV['EXCLUDE_SERVICES'] || 'consul-agent-http,mesos-slave,mesos-agent-watcher,mesos-exporter-slave').split(',')
5
5
 
6
6
  @current_time = Time.now.utc.iso8601
7
+ @last_service_info = {}
8
+ @newest_index = 0 unless @newest_index
7
9
  cur_state = services.map do |service_name, _tags|
8
10
  next if services_blacklist.include?(service_name)
9
11
 
@@ -14,6 +16,9 @@ cur_state = services.map do |service_name, _tags|
14
16
  'critical' => 0,
15
17
  'total' => snodes.count
16
18
  }
19
+ cur_index = snodes.endpoint.x_consul_index.to_i
20
+ @newest_index = cur_index if @newest_index < cur_index
21
+ @last_service_info[service_name] = { 'idx' => cur_index, 'stats' => cur_stats }
17
22
  snodes.each do |snode|
18
23
  case snode.status.downcase
19
24
  when 'passing'
@@ -32,9 +37,9 @@ cur_state = services.map do |service_name, _tags|
32
37
  'address' => instance.service_address,
33
38
  'node' => instance['Node']['Node'],
34
39
  'port' => instance['Service']['Port'],
35
- 'idx' => instance['Service']['ModifyIndex'],
40
+ 'idx' => (instance['Service']['ModifyIndex'] || cur_index).to_i,
36
41
  'status' => instance.status,
37
- 'stats' => cur_stats,
42
+ 'stats' => cur_stats,
38
43
  'checks' => instance['Checks'].map { |check| [check['CheckID'], { 'name' => check['Name'], 'status' => check['Status'], 'output' => check['Output'] }] }.to_h
39
44
  }]
40
45
  end.to_h
@@ -54,26 +59,27 @@ class RingBuffer < Array
54
59
 
55
60
  def initialize(max_size:, enum: nil)
56
61
  @max_size = max_size
62
+ warn "Ringbuffer initialized with #{max_size}"
57
63
  enum&.each { |e| self << e }
58
64
  end
59
65
 
60
66
  def <<(element)
61
- if size < @max_size || @max_size.nil?
62
- super
63
- else
67
+ return unless element
68
+ if size >= @max_size
64
69
  shift
70
+ end
71
+ previous_e = last
72
+ after = []
73
+ while !previous_e.nil? && (previous_e['idx'] > element['idx'])
74
+ after.insert(0, pop)
65
75
  previous_e = last
66
- after = []
67
- while !previous_e.nil? && previous_e['idx'] > element['idx']
68
- after << pop
69
- previous_e = last
70
- end
71
- push(element)
72
- push after
73
76
  end
77
+ push(element)
78
+ after.each do |x|
79
+ push(x)
80
+ end
81
+ self
74
82
  end
75
-
76
- alias push <<
77
83
  end
78
84
 
79
85
  def diff(old, new_e)
@@ -92,16 +98,12 @@ def log_event(line)
92
98
  puts "#{Time.now.to_i} #{line}" if ENV['DEBUG_TIMELINE']
93
99
  end
94
100
 
95
- def store_event(service: service_name, instance: nil, old_state: nil, new_state: nil, instance_info: nil, checks: [])
96
- STDERR.puts "empty instance_info for #{service} ; #{instance} ; #{new_state}" unless instance_info
97
- @new_events << { 'service' => service, 'instance' => instance, 'idx' => instance_info['idx'], 'old_state' => old_state, 'new_state' => new_state,
101
+ def store_event(service: nil, instance: nil, old_state: nil, new_state: nil, instance_info: nil, checks: [])
102
+ @new_events << { 'service' => service, 'instance' => instance, 'old_state' => old_state, 'new_state' => new_state,
98
103
  'ts' => @current_time, 'instance_info' => instance_info }.tap do |ev|
99
104
  ev['checks'] = checks if checks
100
- ev['stats'] = if instance_info
101
- instance_info['stats']
102
- else
103
- {}
104
- end
105
+ ev['stats'] = instance_info['stats']
106
+ ev['idx'] = instance_info['idx']
105
107
  end
106
108
  end
107
109
 
@@ -133,11 +135,19 @@ service_diff = diff(old_state.keys, cur_state.keys)
133
135
  service_diff.disappeared.each do |service_name|
134
136
  old_state[service_name].each do |instance_name, instance_info|
135
137
  checks = compute_checks(old_state, cur_state, service_name, instance_name)
138
+ the_info = {}.merge(instance_info)
139
+ the_info['idx'] = @newest_index
140
+ the_info['stats'] = {
141
+ 'passing' => 0,
142
+ 'warning' => 0,
143
+ 'critical' => 0,
144
+ 'total' => 0,
145
+ }
136
146
  store_event(service: service_name,
137
147
  instance: instance_name,
138
148
  old_state: old_state[service_name][instance_name]['status'],
139
149
  new_state: nil,
140
- instance_info: instance_info,
150
+ instance_info: the_info,
141
151
  checks: checks)
142
152
  end
143
153
  end
@@ -156,11 +166,14 @@ end
156
166
 
157
167
  instance_diff.disappeared.each do |instance_name|
158
168
  checks = compute_checks(old_state, cur_state, service_name, instance_name)
169
+ the_info = {}.merge(old_state[service_name][instance_name])
170
+ the_info['idx'] = @last_service_info[service_name]['idx']
171
+ the_info['stats'] = @last_service_info[service_name]['stats']
159
172
  store_event(service: service_name,
160
173
  old_state: old_state[service_name][instance_name]['status'],
161
174
  new_state: nil,
162
175
  instance: instance_name,
163
- instance_info: old_state[service_name][instance_name],
176
+ instance_info: the_info,
164
177
  checks: checks)
165
178
  end
166
179
  instance_diff.appeared.each do |instance_name|
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.8.8
4
+ version: 1.9.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: 2018-12-20 00:00:00.000000000 Z
11
+ date: 2019-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request