consul-templaterb 1.8.2 → 1.8.3
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 +7 -0
- data/lib/consul/async/version.rb +1 -1
- data/samples/consul-ui/consul-timeline-ui.html.erb +6 -8
- data/samples/consul-ui/css/style.css +40 -1
- data/samples/consul-ui/js/timeline.js +118 -54
- data/samples/consul-ui/js/utils.js +9 -0
- data/samples/consul-ui/timeline.json.erb +4 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68013fd942d003c33f69526b5061fa9f6ce149332bf97db49978e2a2bc9628e0
|
4
|
+
data.tar.gz: d38f04e18ed0c9a7a051804d8cdae8826140ee6161fa17ce400ba5943c8465b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30f1983821cdd6300dd7f6c2610c9936e70d7f110dc4a83afa36b75256d652bdcf59dc20f378d4e3918f8473934e5c01e8ce17f4724d72e96a6ac0c0c77438b9
|
7
|
+
data.tar.gz: 4c0695f4f2a2f0e0ec4695394e6845a8a1880e7745d73b70b400f2cf2039f98f5005f82bc0e1b6bc964909efd81a4b140c6104e44503567b12565fb804cb89b5
|
data/CHANGELOG.md
CHANGED
data/lib/consul/async/version.rb
CHANGED
@@ -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="
|
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
|
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…</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="
|
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 Status
|
38
|
-
<th colspan="2" scope="col">
|
35
|
+
<th scope="col">Service 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.
|
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,
|
24
|
-
|
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(
|
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
|
-
|
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
|
79
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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
|
296
|
-
|
297
|
-
|
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
|
-
|
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
|
-
|
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
|
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'] ||
|
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.
|
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.
|
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
|