consul-templaterb 1.8.2 → 1.8.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d652b0259f34bd70784ebdfbfe18e042bb48bdd03af5f5d920a71b94bb1281d
4
- data.tar.gz: 00116db5cf7b2f11516abb912aab335c2608ec01fdf9ba527f11deb9bc861755
3
+ metadata.gz: 68013fd942d003c33f69526b5061fa9f6ce149332bf97db49978e2a2bc9628e0
4
+ data.tar.gz: d38f04e18ed0c9a7a051804d8cdae8826140ee6161fa17ce400ba5943c8465b5
5
5
  SHA512:
6
- metadata.gz: c53f3a2704b5b52b3febb4c4844c36dae660b163d1de8298acde2f88c0cb9b9e65bc202d35c160d0747b3624bfa1355c0e014ef58e2d31673a5b40d17b0d8847
7
- data.tar.gz: bc16ad7b30485b403c19d01251c87ffa66d17b937b09affdf2c74470455283d5121b95909524ec703d29239efe97b626f7e0c70bb34090a04bab69ad224a176c
6
+ metadata.gz: 30f1983821cdd6300dd7f6c2610c9936e70d7f110dc4a83afa36b75256d652bdcf59dc20f378d4e3918f8473934e5c01e8ce17f4724d72e96a6ac0c0c77438b9
7
+ data.tar.gz: 4c0695f4f2a2f0e0ec4695394e6845a8a1880e7745d73b70b400f2cf2039f98f5005f82bc0e1b6bc964909efd81a4b140c6104e44503567b12565fb804cb89b5
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## (UNRELEASED)
4
4
 
5
+ ## 1.8.3 (December 18, 2018)
6
+
7
+ OPTIMIZATIONS:
8
+
9
+ * Consul timeline now autorefresh itself
10
+ * large performance improvement in Consul
11
+
5
12
  ## 1.8.2 (December 18, 2018)
6
13
 
7
14
  NEW FEATURES:
@@ -1,5 +1,5 @@
1
1
  module Consul
2
2
  module Async
3
- VERSION = '1.8.2'.freeze
3
+ VERSION = '1.8.3'.freeze
4
4
  end
5
5
  end
@@ -6,23 +6,21 @@
6
6
  <div id="filter-menu" class="col-2 col-m-3 px-4 pt-4">
7
7
  <div class="form-group">
8
8
  <div class="input-group">
9
- <input id="service-filter" type="text" placeholder="filter instances" class="form-control" />
10
- <div class="input-group-append">
11
- <span class="input-group-text" id="service-counter"></span>
12
- </div>
9
+ <input id="service-filter" type="search" placeholder="filter services" class="form-control" />
13
10
  </div>
14
11
  </div>
15
12
  <div id="service-wrapper" >
16
13
  <ul id="service-list" class="list-group">
17
- <li onfocus="serviceTimeline.selectService(this)" onclick="serviceTimeline.selectService(this)" value="" class="serviceListItem list-group-item list-group-item-actionn active" id="anyService"><div class="statuses float-right"><span class="lookup badge badge-pill badge-dark">1000</span></div><div class="service-name">All</div></li>
14
+ <li onfocus="serviceTimeline.selectService(this)" onclick="serviceTimeline.selectService(this)" value="" class="serviceListItem list-group-item list-group-item-actionn" id="anyService"><div class="statuses float-right"><span id="allServicesCount" class="lookup badge badge-pill badge-secondary">loading&hellip;</span></div><div class="service-name">All</div></li>
18
15
  </ul>
19
16
  </div>
20
17
  </div>
21
18
  <div class="col-10 col-m-9">
19
+ <div id="autorefresh"><input id="autorefresh-check" type="checkbox" checked><label for="autorefresh-check">Autorefresh</label></div>
22
20
  <h2 class="text-center" id="service-title"></h2>
23
21
  <div class="row mb-2">
24
22
  <div class="input-group float-left col-12">
25
- <input id="instance-filter" type="text" placeholder="filter nodes by name or tags" class="form-control" />
23
+ <input id="instance-filter" type="search" placeholder="filter events" class="form-control" />
26
24
  </div>
27
25
  </div>
28
26
  <div id="instances-wrapper">
@@ -34,8 +32,8 @@
34
32
  <th scope="col" class="serviceCol">Service</th>
35
33
  <th scope="col">Instance</th>
36
34
  <th scope="col">Check State</th>
37
- <th scope="col">Service&nbsp;Status&nbsp;Change</th>
38
- <th colspan="2" scope="col">Service&nbsp;Instances</th>
35
+ <th scope="col">Service&nbsp;Status</th>
36
+ <th id="all-instances-header" colspan="2" scope="col">Instances</th>
39
37
  </tr>
40
38
  </thead>
41
39
  <tbody>
@@ -17,6 +17,39 @@
17
17
  text-overflow: ellipsis;
18
18
  overflow: hidden;
19
19
  }
20
+ .checkTransition {
21
+ float: left;
22
+ }
23
+ .checkName {
24
+ padding-left: 5px;
25
+ overflow: hidden;
26
+ white-space: nowrap;
27
+ text-overflow: ellipsis;
28
+ }
29
+ td.serviceName {
30
+ max-width: 15%;
31
+ overflow: hidden;
32
+ text-overflow: ellipsis;
33
+ }
34
+ #autorefresh{
35
+ float: right;
36
+ }
37
+ td.instance {
38
+ max-width: 15em;
39
+ overflow: hidden;
40
+ text-overflow: ellipsis;
41
+ }
42
+ #all-instances-header {
43
+ width: 12em;
44
+ text-align: right;
45
+ padding-right: 1em;
46
+ }
47
+ td.ts {
48
+ width: 9em;
49
+ }
50
+ td.serviceName {
51
+ max-width: 12em;
52
+ }
20
53
 
21
54
  #service-list .favorite {
22
55
  width: 20px;
@@ -60,7 +93,13 @@
60
93
  border-left: 0px;
61
94
  border-right: 0px;
62
95
  }
63
-
96
+ .service-status {
97
+ width: 10em;
98
+ }
99
+ .all-instances {
100
+ width: 14em;
101
+ text-align: right;
102
+ }
64
103
  #node-statuses span {
65
104
  font-size: 1rem;
66
105
  transition: background-color .16s linear;
@@ -2,7 +2,7 @@
2
2
  class ServiceTimeline {
3
3
  constructor(ressourceURL, refresh) {
4
4
  this.ressourceURL = ressourceURL;
5
- this.fetchRessource();
5
+ this.fetchRessource(true);
6
6
  this.serviceList = $("#service-list");
7
7
  this.serviceFilter = $("#service-filter");
8
8
  this.serviceFilter.keyup(this.filterService);
@@ -12,26 +12,34 @@ class ServiceTimeline {
12
12
  this.refresh = parseInt(refresh);
13
13
  this.filterStatus = null;
14
14
  this.refreshTimeout = null;
15
- this.serviceFilterCounter = $("#service-counter");
16
- this.serviceFilterCount = 0;
15
+ this.reloadData = null;
17
16
  this.services = {}
17
+ this.lastEntryLoaded = null;
18
18
  this.presentServices = {}
19
+ this.currentlyUpdating = false;
19
20
  var sT = this;
20
21
  }
21
22
 
22
- fetchRessource() {
23
- $.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this, success: function(result){
24
- serviceTimeline.initRessource(result);
25
- }});
23
+ fetchRessource(firstReload) {
24
+ $.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this,
25
+ success: function(result){
26
+ serviceTimeline.initRessource(result, firstReload);
27
+ },
28
+ error: function(err) {
29
+ console.log("Error updating data", err);
30
+ serviceTimeline.currentlyUpdating = false;
31
+ }
32
+ });
26
33
  }
27
34
 
28
- initRessource(data) {
35
+ initRessource(data, firstReload) {
29
36
  this.data = data;
30
- this.reloadTimeline(true);
37
+ this.reloadTimeline(firstReload);
31
38
  }
32
39
 
33
40
  createServiceDefItem(label, serviceName, counter) {
34
41
  var listItem = document.createElement('li');
42
+ listItem.setAttribute('id', 'service-item-filter-' + serviceName);
35
43
  listItem.setAttribute('onfocus','serviceTimeline.selectService(this, true)');
36
44
  listItem.setAttribute('onclick','serviceTimeline.selectService(this, true)');
37
45
  listItem.setAttribute('value', serviceName);
@@ -44,17 +52,25 @@ class ServiceTimeline {
44
52
 
45
53
  var statuses = document.createElement('div');
46
54
  statuses.setAttribute('class','statuses float-right');
47
- statuses.appendChild(this.createBadge(counter, 'dark'));
55
+ statuses.appendChild(this.createBadge(counter, 'dark instanceCounter'));
48
56
  listItem.prepend(statuses);
49
57
  return listItem;
50
58
  }
51
59
 
60
+ reloadDataFromJSON(){
61
+ if (document.getElementById('autorefresh-check').checked) {
62
+ if (serviceTimeline.currentlyUpdating == false) {
63
+ serviceTimeline.currentlyUpdating = true;
64
+ serviceTimeline.fetchRessource(false);
65
+ }
66
+ }
67
+ }
68
+
52
69
  reloadTimeline(firstReload) {
53
70
  if (!this.data) {
54
71
  console.log("No data to display");
72
+ serviceTimeline.currentlyUpdating = false;
55
73
  }
56
- this.serviceList.html('');
57
- this.serviceFilterCount = 0;
58
74
  var servicesPerName = {};
59
75
  var numberOfEvents = this.data.length;
60
76
  for (var i = 0 ; i < numberOfEvents; i++) {
@@ -68,17 +84,22 @@ class ServiceTimeline {
68
84
  }
69
85
  var sorted = Object.keys(servicesPerName).sort();
70
86
  var serviceListItems = this.serviceList[0];
71
- var allServices = this.createServiceDefItem('All', '', numberOfEvents);
72
- allServices.setAttribute('id', 'anyService');
73
- serviceListItems.appendChild(allServices);
87
+ $('#allServicesCount').html(numberOfEvents)
74
88
  this.presentServices = servicesPerName;
75
89
  for (var i = 0 ; i < sorted.length; i++) {
76
90
  var serviceName = sorted[i];
77
91
  var counter = servicesPerName[serviceName];
78
- var listItem = this.createServiceDefItem(serviceName, serviceName, counter);
79
- serviceListItems.appendChild(listItem);
92
+ var existing = $('#service-item-filter-' + serviceName);
93
+ if (existing.length > 0) {
94
+ $('#service-item-filter-' + serviceName+ ' .instanceCounter').html(counter);
95
+ } else {
96
+ var listItem = this.createServiceDefItem(serviceName, serviceName, counter);
97
+ serviceListItems.appendChild(listItem);
98
+ }
99
+
80
100
  }
81
- this.displayEvents();
101
+ this.displayEvents(firstReload);
102
+ serviceTimeline.currentlyUpdating = false;
82
103
  if (firstReload) {
83
104
  var sT = this;
84
105
  setTimeout(function(){
@@ -111,7 +132,10 @@ class ServiceTimeline {
111
132
  if (!found) {
112
133
  sT.selectService($('#anyService', false));
113
134
  }
135
+ setInterval(sT.reloadDataFromJSON, 10000);
114
136
  }, 150);
137
+ } else {
138
+ this.doFilter();
115
139
  }
116
140
  }
117
141
 
@@ -146,8 +170,7 @@ class ServiceTimeline {
146
170
  return span;
147
171
  }
148
172
 
149
- doFilter() {
150
- var filterValue = $('#instance-filter')[0].value;
173
+ performFiltering(filterValue) {
151
174
  this.refreshTimeout = null;
152
175
  var matcher;
153
176
  try {
@@ -199,6 +222,16 @@ class ServiceTimeline {
199
222
  }
200
223
  }
201
224
 
225
+ doFilter() {
226
+ var filterValue = $('#instance-filter')[0].value;
227
+ if (this.refreshTimeout) {
228
+ clearTimeout(this.refreshTimeout);
229
+ }
230
+ this.refreshTimeout = window.setTimeout(function(){
231
+ serviceTimeline.performFiltering(filterValue);
232
+ }, 16);
233
+ }
234
+
202
235
  selectService(source, updateUrl) {
203
236
  $(this.selectedService).removeClass('active');
204
237
  var serviceName = $(source).find(".service-name").html()
@@ -236,7 +269,6 @@ class ServiceTimeline {
236
269
  console.log("Failed to compile regexp for '" + serviceVal + "', using strict lookup due to: " + e);
237
270
  filter = new RegExp(safeReg);
238
271
  }
239
- serviceTimeline.serviceFilterCount = 0;
240
272
  var showProxiesInList = this.showProxiesInList;
241
273
  serviceTimeline.serviceList.children('.serviceListItem').each(function (){
242
274
  var ui = $(this);
@@ -250,17 +282,50 @@ class ServiceTimeline {
250
282
  });
251
283
  }
252
284
 
253
- displayEvents() {
285
+ displayEvents(firstReload) {
254
286
  //$("#service-title").html(service['name']);
255
287
  var tableBody = $('#all-events > tbody');
256
- tableBody.html("");
288
+ var startIndex = 0;
289
+ if (firstReload || this.lastEntryLoaded == null) {
290
+ tableBody.html("");
291
+ } else {
292
+ // We first try to find new entries...
293
+ var o = this.lastEntryLoaded;
294
+ for (var i = 0 ; i < this.data.length; i++) {
295
+ var e = this.data[i];
296
+ if (o.ts == e.ts && o.instance == e.instance) {
297
+ startIndex = i + 1;
298
+ console.log('Resuming at ', startIndex, " with ", e);
299
+ break;
300
+ }
301
+ }
302
+ }
257
303
  var tbody = tableBody[0];
258
304
  var filter = "";
259
- for (var i = 0 ; i < this.data.length; i++) {
305
+ var lastEntryFound = null;
306
+ for (var i = startIndex ; i < this.data.length; i++) {
260
307
  var e = this.data[i];
308
+ lastEntryFound = e;
261
309
  var row = document.createElement('tr');
262
310
  row.setAttribute("class", 'srv-' + e.service);
263
- this.buildCell(row, 'td', 'ts', this.appChild('time', document.createTextNode(e.ts)));
311
+ var timestamp;
312
+ var fullTimestamp;
313
+ try {
314
+ var tsMs = Date.parse(e.ts);
315
+ if (isNaN(tsMs)) {
316
+ timestamp = e.ts;
317
+ fullTimestamp = e.ts;
318
+ } else {
319
+ fullTimestamp = new Date(tsMs);
320
+ timestamp = formatDate(fullTimestamp);
321
+ }
322
+ } catch (err){ console.log("Failed parsing date", e.ts, err);}
323
+ {
324
+ var timeElement = this.appChild('time', document.createTextNode(timestamp));
325
+ timeElement.setAttribute('datetime', fullTimestamp);
326
+ timeElement.setAttribute('title', fullTimestamp);
327
+ this.buildCell(row, 'td', 'ts', timeElement);
328
+ }
264
329
  this.buildCell(row, 'td', 'lookup serviceName serviceCol', document.createTextNode(e.service));
265
330
  var text = e.instance;
266
331
  if (e.instance_info && e.instance_info.node) {
@@ -272,39 +337,38 @@ class ServiceTimeline {
272
337
  var instanceCell = this.buildCell(row, 'td', 'lookup instance', document.createTextNode(text));
273
338
  instanceCell.setAttribute("title", e.instance);
274
339
  {
275
- var checksCell = document.createElement("div")
276
- this.buildCell(row, 'td', 'lookup checks', checksCell);
277
- for (var j = 0; j < e.checks.length; j++) {
278
- var c = e.checks[j];
279
- var statusSpan = document.createElement('div');
280
- statusSpan.setAttribute('class', 'checkTransition');
281
- statusSpan.appendChild(this.createBadge(c['old']));
282
- statusSpan.appendChild(document.createTextNode(''));
283
- var newBadge = this.createBadge(c['new']);
284
- newBadge.setAttribute('title', c['output']);
285
- statusSpan.appendChild(newBadge);
286
- checksCell.appendChild(statusSpan);
287
- var checkName = document.createElement('div');
288
- checkName.setAttribute('class', 'lookup checkName');
289
- checkName.setAttribute('title', c['id']);
290
- checkName.appendChild(document.createTextNode(c['name']));
291
- checksCell.appendChild(checkName);
340
+ var checksCell;
341
+ if (e.checks.length == 0) {
342
+ checksCell = this.createBadge('No check modified', 'primary');
343
+ } else {
344
+ checksCell = document.createElement("div");
345
+ for (var j = 0; j < e.checks.length; j++) {
346
+ var c = e.checks[j];
347
+ var statusSpan = document.createElement('div');
348
+ statusSpan.setAttribute('class', 'checkTransition');
349
+ statusSpan.appendChild(this.createBadge(c['old']));
350
+ statusSpan.appendChild(document.createTextNode('→'));
351
+ var newBadge = this.createBadge(c['new']);
352
+ statusSpan.appendChild(newBadge);
353
+ checksCell.appendChild(statusSpan);
354
+ var checkName = document.createElement('div');
355
+ checkName.setAttribute('class', 'lookup checkName');
356
+ checkName.setAttribute('data-toggle', 'tooltip');
357
+ checkName.setAttribute('title', c['id']+'\n\n' + c.output);
358
+ checkName.appendChild(document.createTextNode(c['name']));
359
+ checksCell.appendChild(checkName);
360
+ }
292
361
  }
362
+ this.buildCell(row, 'td', 'lookup checks', checksCell);
293
363
  }
294
364
  {
295
- var statusSpan = document.createElement('span');
296
- statusSpan.appendChild(this.createBadge(e['old_state']));
297
- statusSpan.appendChild(document.createTextNode(''));
298
- statusSpan.appendChild(this.createBadge(e['new_state']));
299
- this.buildCell(row, 'td', 'status', statusSpan);
365
+ var td = this.buildCell(row, 'td', 'service-status', this.createBadge(e['old_state']));
366
+ td.appendChild(document.createTextNode(''));
367
+ td.appendChild(this.createBadge(e['new_state']));
300
368
  }
301
369
  {
302
- var allInstances = document.createElement('span');
303
370
  var nSuccess = e['stats']['passing'];
304
- if (nSuccess > 0) {
305
- var success = this.createBadge(nSuccess, 'success');
306
- allInstances.appendChild(success);
307
- }
371
+ var allInstances = this.buildCell(row, 'td', 'all-instances', this.createBadge(nSuccess, 'success'));
308
372
  var nWarnings = e['stats']['warning'];
309
373
  if (nWarnings > 0) {
310
374
  var elem = this.createBadge(nWarnings, 'warning');
@@ -325,10 +389,10 @@ class ServiceTimeline {
325
389
  } else if (percent < 50) {
326
390
  clazz = 'warning';
327
391
  }
328
- allInstances.appendChild(this.createBadge(percent + " %", clazz));
329
- row.appendChild(allInstances);
392
+ this.buildCell(row, 'td', 'ipercents', this.createBadge(percent + " %", clazz));
330
393
  }
331
394
  tbody.prepend(row)
332
395
  }
396
+ this.lastEntryLoaded = lastEntryFound;
333
397
  }
334
398
  }
@@ -12,6 +12,15 @@ function buildServiceStatus(service) {
12
12
  return serviceStatus;
13
13
  }
14
14
 
15
+ function padDateUnit(x) {
16
+ return x > 9 ? x : '0' + x;
17
+ }
18
+
19
+ function formatDate(date) {
20
+ return padDateUnit(date.getMonth()+1) + "/" + padDateUnit(date.getDate()) + " " + padDateUnit(date.getHours()) + ':' + padDateUnit(date.getMinutes()) + ':' + padDateUnit(date.getSeconds());
21
+ }
22
+
23
+
15
24
  function nodeState(checks) {
16
25
  status='passing';
17
26
  for (var checkKey in checks) {
@@ -1,13 +1,10 @@
1
1
  <%
2
2
  require 'json'
3
+ services_blacklist = (ENV['EXCLUDE_SERVICES'] || 'consul-agent-http,mesos-slave,mesos-agent-watcher,mesos-exporter-slave').split(',')
3
4
 
4
- @current_time = Time.now.utc
5
+ @current_time = Time.now.utc.iso8601
5
6
  cur_state = services.map do |service_name, _tags|
6
- next if service_name.match?(/^lbl7-/)
7
- next if service_name.match?(/^wmi-/)
8
- next if service_name.match?(/collectd-/)
9
- next if service_name.match?(/-(eu|as|us)$/)
10
- next if service_name.match?(/-admin$/)
7
+ next if services_blacklist.include?(service_name)
11
8
 
12
9
  snodes = service(service_name)
13
10
  cur_stats = {
@@ -79,7 +76,7 @@ def diff(old, new)
79
76
  diff
80
77
  end
81
78
 
82
- @events = RingBuffer.new(max_size: (ENV['CONSUL_TIMELINE_BUFFER'] || 1000).to_i) unless @events
79
+ @events = RingBuffer.new(max_size: (ENV['CONSUL_TIMELINE_BUFFER'] || 10000).to_i) unless @events
83
80
 
84
81
  def log_event(line)
85
82
  puts "#{Time.now.to_i} #{line}" if ENV['DEBUG_TIMELINE']
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: consul-templaterb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: 1.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - SRE Core Services
@@ -231,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
231
  version: '0'
232
232
  requirements: []
233
233
  rubyforge_project:
234
- rubygems_version: 2.7.7
234
+ rubygems_version: 2.7.8
235
235
  signing_key:
236
236
  specification_version: 4
237
237
  summary: Implementation of Consul template using Ruby and .erb templating language