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 +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
|