consul-templaterb 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/bin/consul-templaterb +43 -4
- data/lib/consul/async/consul_endpoint.rb +4 -34
- data/lib/consul/async/consul_template.rb +54 -13
- data/lib/consul/async/consul_template_engine.rb +2 -1
- data/lib/consul/async/endpoint.rb +137 -0
- data/lib/consul/async/stats.rb +40 -0
- data/lib/consul/async/vault_endpoint.rb +249 -0
- data/lib/consul/async/version.rb +1 -1
- data/null/ruby-type-inference/ruby-type-inference.mv.db +0 -0
- data/samples/consul-ui/common/header.html.erb +20 -1
- data/samples/consul-ui/consul-keys-ui.html.erb +39 -0
- data/samples/consul-ui/consul-nodes-ui.html.erb +35 -0
- data/samples/consul-ui/consul-services-ui.html.erb +9 -3
- data/samples/consul-ui/consul_keys.json.erb +12 -0
- data/samples/consul-ui/consul_nodes.json.erb +64 -0
- data/samples/consul-ui/{consul_template.json.erb → consul_services.json.erb} +0 -0
- data/samples/consul-ui/css/style.css +80 -4
- data/samples/consul-ui/js/keys.js +129 -0
- data/samples/consul-ui/js/nodes.js +120 -0
- data/samples/consul-ui/js/service.js +53 -179
- data/samples/consul-ui/js/utils.js +347 -0
- data/samples/consul-ui/vendors/highlight/atom-one-dark.css +96 -0
- data/samples/consul-ui/vendors/highlight/highlight.pack.js +2 -0
- data/samples/criteo/vault-test.erb +6 -0
- metadata +17 -3
@@ -0,0 +1,12 @@
|
|
1
|
+
<% path = ENV['kv_path'] || ''
|
2
|
+
require 'base64'
|
3
|
+
require 'json'
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
data = {}
|
7
|
+
kv(path, recurse:true).each do |tuple|
|
8
|
+
data[tuple['Key']] = tuple['Value']
|
9
|
+
end
|
10
|
+
%><%
|
11
|
+
json = { kv: data, generated_at: Time.now}
|
12
|
+
%><%= JSON.pretty_generate(json) %>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<%
|
2
|
+
# This template can be configure the following way with environment variables
|
3
|
+
# Environment variables to filter services/instances
|
4
|
+
# SERVICES_TAG_FILTER: basic tag filter for service (default HTTP)
|
5
|
+
# INSTANCE_MUST_TAG: Second level of filtering (optional, default to SERVICES_TAG_FILTER)
|
6
|
+
# INSTANCE_EXCLUDE_TAG: Exclude instances having the given tag (default: canary)
|
7
|
+
# EXCLUDE_SERVICES: comma-separated services to exclude (default: consul-agent-http,mesos-slave,mesos-agent-watcher)
|
8
|
+
|
9
|
+
service_tag_filter = ENV['SERVICES_TAG_FILTER'] || nil
|
10
|
+
instance_must_tag = ENV['INSTANCE_MUST_TAG'] || service_tag_filter
|
11
|
+
instance_exclude_tag = ENV['INSTANCE_EXCLUDE_TAG']
|
12
|
+
|
13
|
+
# Services to hide
|
14
|
+
services_blacklist = (ENV['EXCLUDE_SERVICES'] || 'consul-agent-http,mesos-slave,mesos-agent-watcher,mesos-exporter-slave').split(',')
|
15
|
+
service_per_node = {}
|
16
|
+
services.each do |service_name, tags|
|
17
|
+
if !services_blacklist.include? service_name
|
18
|
+
service(service_name, tag: service_tag_filter).sort {|a,b| a['Node']['Node'] <=> b['Node']['Node'] }.each do |snode|
|
19
|
+
node_info = service_per_node[snode['Node']['Node']] || {}
|
20
|
+
|
21
|
+
node_node_data = {
|
22
|
+
Name: snode['Node']['Node'],
|
23
|
+
Address: snode['Node']['Address'],
|
24
|
+
Meta: snode['Node']['Meta'],
|
25
|
+
}
|
26
|
+
|
27
|
+
node_services_data = node_info[:Service] || []
|
28
|
+
|
29
|
+
service_data = {
|
30
|
+
Service: snode["Service"]["Service"],
|
31
|
+
Address: snode.service_address,
|
32
|
+
Tags: snode["Service"]["Tags"],
|
33
|
+
Port: snode["Service"]["Port"],
|
34
|
+
}
|
35
|
+
checks_array = []
|
36
|
+
snode["Checks"].each do |check|
|
37
|
+
checks_data = {
|
38
|
+
name: check["Name"],
|
39
|
+
status: check["Status"],
|
40
|
+
notes: check["Notes"],
|
41
|
+
output: check["Output"],
|
42
|
+
}
|
43
|
+
checks_array.push(checks_data)
|
44
|
+
end
|
45
|
+
node_service = {
|
46
|
+
Service: service_data,
|
47
|
+
Checks: checks_array,
|
48
|
+
}
|
49
|
+
node_services_data.push(node_service)
|
50
|
+
|
51
|
+
node_data = {
|
52
|
+
Node: node_node_data,
|
53
|
+
Service: node_services_data,
|
54
|
+
}
|
55
|
+
|
56
|
+
service_per_node[snode['Node']['Node']] = node_data
|
57
|
+
end
|
58
|
+
# break
|
59
|
+
end
|
60
|
+
end
|
61
|
+
%><%
|
62
|
+
json_datacenters = datacenters
|
63
|
+
json = { nodes: service_per_node, datacenters: json_datacenters, generated_at: Time.now}
|
64
|
+
%><%= JSON.pretty_generate(json) %>
|
File without changes
|
@@ -3,7 +3,7 @@
|
|
3
3
|
box-shadow: none;
|
4
4
|
}
|
5
5
|
|
6
|
-
#service-wrapper, #instances-wrapper {
|
6
|
+
#service-wrapper, #instances-wrapper, #keys-wrapper {
|
7
7
|
overflow: scroll;
|
8
8
|
border-top-left-radius: 0px;
|
9
9
|
border-top-right-radius: 0px;
|
@@ -18,23 +18,35 @@
|
|
18
18
|
overflow: hidden;
|
19
19
|
}
|
20
20
|
|
21
|
-
#service-wrapper
|
21
|
+
#service-wrapper .list-group-item:last-child, #instances-wrapper .list-group-item:last-child,
|
22
|
+
#keys-wrapper .list-group-item:last-child {
|
22
23
|
border-bottom-width: 0px;
|
23
24
|
border-bottom-left-radius: 0px;
|
24
25
|
border-bottom-right-radius: 0px;
|
25
26
|
}
|
26
27
|
|
27
|
-
#service-wrapper
|
28
|
+
#service-wrapper .list-group-item:first-child, #instances-wrapper .list-group-item:first-child,
|
29
|
+
#keys-wrapper .list-group-item:first-child {
|
28
30
|
border-top-width: 0px;
|
29
31
|
border-top-left-radius: 0px;
|
30
32
|
border-top-right-radius: 0px;
|
31
33
|
}
|
32
34
|
|
33
|
-
#service-wrapper
|
35
|
+
#service-wrapper .list-group-item, #instances-wrapper .list-group-item, #keys-wrapper .list-group-item {
|
34
36
|
border-left: 0px;
|
35
37
|
border-right: 0px;
|
36
38
|
}
|
37
39
|
|
40
|
+
#node-statuses span {
|
41
|
+
font-size: 1rem;
|
42
|
+
transition: background-color .16s linear;
|
43
|
+
cursor: pointer;
|
44
|
+
}
|
45
|
+
|
46
|
+
#node-statuses {
|
47
|
+
margin-top: 0.4rem;
|
48
|
+
}
|
49
|
+
|
38
50
|
h2 {
|
39
51
|
margin-top: 1rem;
|
40
52
|
margin-bottom: 1rem;
|
@@ -75,3 +87,67 @@ h2 .fas:hover {
|
|
75
87
|
background-color: #a5a5a5 !important;
|
76
88
|
color: #f1f1f1 !important;
|
77
89
|
}
|
90
|
+
|
91
|
+
.status-deactivated {
|
92
|
+
background-color: #a5a5a5 !important;
|
93
|
+
color: #f1f1f1 !important;
|
94
|
+
}
|
95
|
+
|
96
|
+
.service-status {
|
97
|
+
transition: background-color .16s linear;
|
98
|
+
font-size: 85%;
|
99
|
+
cursor: pointer;
|
100
|
+
}
|
101
|
+
|
102
|
+
#instance-statuses {
|
103
|
+
padding-top: 5px;
|
104
|
+
text-align: center;
|
105
|
+
}
|
106
|
+
|
107
|
+
.status-sidebar {
|
108
|
+
width: 5px !important;
|
109
|
+
min-height: 1px;
|
110
|
+
flex-shrink: 0;
|
111
|
+
flex-grow: 0;
|
112
|
+
}
|
113
|
+
|
114
|
+
.instance-content {
|
115
|
+
width: 100%;
|
116
|
+
padding-top: 12px;
|
117
|
+
padding-bottom: 12px;
|
118
|
+
padding-left: 12px;
|
119
|
+
}
|
120
|
+
|
121
|
+
#nodes #instances-list .list-group-item {
|
122
|
+
padding-left: 0px;
|
123
|
+
padding-top: 0px;
|
124
|
+
padding-bottom: 0px;
|
125
|
+
display: flex;
|
126
|
+
flex-direction: row;
|
127
|
+
}
|
128
|
+
|
129
|
+
html, body {
|
130
|
+
height:100%;
|
131
|
+
}
|
132
|
+
|
133
|
+
.instance-content-header {
|
134
|
+
display: flex;
|
135
|
+
}
|
136
|
+
|
137
|
+
.instance-content-header h5 {
|
138
|
+
margin-right: 10px;
|
139
|
+
}
|
140
|
+
|
141
|
+
.meta-tags {
|
142
|
+
text-align: right;
|
143
|
+
flex: 2 0 0;
|
144
|
+
}
|
145
|
+
|
146
|
+
#data-wrapper {
|
147
|
+
overflow: scroll;
|
148
|
+
}
|
149
|
+
|
150
|
+
pre, code {
|
151
|
+
height: 100%;
|
152
|
+
margin-bottom: 0px;
|
153
|
+
}
|
@@ -0,0 +1,129 @@
|
|
1
|
+
class ConsulKeys {
|
2
|
+
constructor(ressourceURL, refresh) {
|
3
|
+
this.ressourceURL = ressourceURL;
|
4
|
+
this.fetchRessource();
|
5
|
+
this.keysList = $("#keys-list");
|
6
|
+
this.keysFilter = $("#keys-filter");
|
7
|
+
this.keysFilter.keyup(this.filterService);
|
8
|
+
this.refresh = parseInt(refresh);
|
9
|
+
this.keysFilterCounter = $("#keys-counter");
|
10
|
+
this.keysFilterCount = 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
fetchRessource() {
|
14
|
+
$.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this, success: function(result){
|
15
|
+
consulKeys.initRessource(result);
|
16
|
+
}});
|
17
|
+
}
|
18
|
+
|
19
|
+
initRessource(data) {
|
20
|
+
this.data = data;
|
21
|
+
this.reloadKeysList();
|
22
|
+
console.log('Data generated at: ' + data['generated_at']);
|
23
|
+
|
24
|
+
var urlParam = new URL(location.href).searchParams.get('key');
|
25
|
+
if (urlParam) {
|
26
|
+
var nodes = document.getElementById('keys-list').childNodes;
|
27
|
+
for(var i in nodes) {
|
28
|
+
if($(nodes[i]).find(".key-name").html() == urlParam) {
|
29
|
+
var selectedElement = $(nodes[i])
|
30
|
+
this.selectKey(selectedElement);
|
31
|
+
selectedElement.focus()
|
32
|
+
break;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
} else {
|
36
|
+
this.selectKey(document.getElementById('keys-list').firstElementChild);
|
37
|
+
}
|
38
|
+
|
39
|
+
if(this.refresh > 0) {
|
40
|
+
setTimeout(this.fetchRessource, this.refresh * 1000);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
reloadKeysList() {
|
45
|
+
this.keysList.html('');
|
46
|
+
this.keysFilterCount = 0;
|
47
|
+
|
48
|
+
for (var key in this.data.kv) {
|
49
|
+
|
50
|
+
var listItem = document.createElement('button');
|
51
|
+
listItem.setAttribute('type','button');
|
52
|
+
listItem.setAttribute('onfocus','consulKeys.onClickServiceName(this)');
|
53
|
+
listItem.setAttribute('onclick','consulKeys.onClickServiceName(this)');
|
54
|
+
listItem.setAttribute('value',key);
|
55
|
+
listItem.setAttribute('class','list-group-item list-group-item-action');
|
56
|
+
|
57
|
+
var serviceNameItem = document.createElement('div');
|
58
|
+
serviceNameItem.setAttribute('class', 'key-name');
|
59
|
+
serviceNameItem.appendChild(document.createTextNode(key));
|
60
|
+
listItem.appendChild(serviceNameItem);
|
61
|
+
|
62
|
+
this.keysFilterCount += 1;
|
63
|
+
this.keysList.append(listItem);
|
64
|
+
}
|
65
|
+
this.keysFilterCounter.html(this.keysFilterCount);
|
66
|
+
resizeWrapper('keys-wrapper', 'keys-list');
|
67
|
+
this.filterService();
|
68
|
+
}
|
69
|
+
|
70
|
+
filterService() {
|
71
|
+
var filter = new RegExp(consulKeys.keysFilter.val());
|
72
|
+
consulKeys.keysFilterCount = 0;
|
73
|
+
consulKeys.keysList.children('button').each(function (){
|
74
|
+
var ui = $(this);
|
75
|
+
if(keyMatcher(this, filter)) {
|
76
|
+
ui.removeClass('d-none');
|
77
|
+
ui.addClass('d-block');
|
78
|
+
consulKeys.keysFilterCount += 1;
|
79
|
+
consulKeys.keysFilterCounter.html(consulKeys.keysFilterCount);
|
80
|
+
} else {
|
81
|
+
ui.removeClass('d-block');
|
82
|
+
ui.addClass('d-none');
|
83
|
+
}
|
84
|
+
})
|
85
|
+
}
|
86
|
+
|
87
|
+
onClickServiceName(source) {
|
88
|
+
this.selectKey(source);
|
89
|
+
this.updateURL($(source).find(".key-name").html());
|
90
|
+
}
|
91
|
+
|
92
|
+
updateURL(link) {
|
93
|
+
var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
|
94
|
+
if (link) {
|
95
|
+
newUrl += '?key=' + link
|
96
|
+
}
|
97
|
+
window.history.pushState({},"",newUrl);
|
98
|
+
}
|
99
|
+
|
100
|
+
selectKey(source) {
|
101
|
+
if (this.selectedKey) {
|
102
|
+
$(this.selectedKey).removeClass('active');
|
103
|
+
}
|
104
|
+
var serviceName = $(source).find(".key-name").html()
|
105
|
+
this.selectedKey = source.closest( "button" );
|
106
|
+
$(this.selectedKey).addClass('active');
|
107
|
+
this.displayKey([serviceName]);
|
108
|
+
}
|
109
|
+
|
110
|
+
displayKey(key) {
|
111
|
+
$("#kv-title").html(key);
|
112
|
+
if(this.data.kv[key] != null) {
|
113
|
+
var dataToDisplay = atob(this.data.kv[key]);
|
114
|
+
} else {
|
115
|
+
var dataToDisplay = 'NO DATA';
|
116
|
+
}
|
117
|
+
|
118
|
+
$("#kv-data").html(dataToDisplay);
|
119
|
+
$("#kv-data").removeClass();
|
120
|
+
|
121
|
+
$('pre code').each(function(i, block) {
|
122
|
+
hljs.highlightBlock(block);
|
123
|
+
});
|
124
|
+
resizeWrapper('data-wrapper', 'kv-data');
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
$( window ).resize(resizeData);
|
129
|
+
resizeData();
|
@@ -0,0 +1,120 @@
|
|
1
|
+
class ConsulNodes {
|
2
|
+
constructor(ressourceURL, refresh) {
|
3
|
+
this.ressourceURL = ressourceURL;
|
4
|
+
this.fetchRessource();
|
5
|
+
this.instanceFilter = $("#instance-filter");
|
6
|
+
this.instanceFilter.keyup(function (e) {
|
7
|
+
clearTimeout(consulNodes.timeout);
|
8
|
+
consulNodes.timeout = setTimeout(consulNodes.filterInstances, 400);
|
9
|
+
});
|
10
|
+
this.refresh = parseInt(refresh);
|
11
|
+
this.filterStatus = null;
|
12
|
+
this.maxDisplayed = 100;
|
13
|
+
this.displayedCount = 0;
|
14
|
+
this.servicesStatus = {};
|
15
|
+
}
|
16
|
+
|
17
|
+
fetchRessource() {
|
18
|
+
$.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this, success: function(result){
|
19
|
+
consulNodes.initRessource(result);
|
20
|
+
}});
|
21
|
+
}
|
22
|
+
|
23
|
+
initRessource(data) {
|
24
|
+
this.data = data;
|
25
|
+
this.displayInstances(this.data['nodes']);
|
26
|
+
console.log('Data generated at: ' + data['generated_at']);
|
27
|
+
}
|
28
|
+
|
29
|
+
onClickFilter(source) {
|
30
|
+
var status = $(source).attr('status');
|
31
|
+
this.filterStatus = (this.filterStatus == status) ? null : status;
|
32
|
+
this.filterInstances();
|
33
|
+
}
|
34
|
+
|
35
|
+
filterInstances() {
|
36
|
+
updateFilterDisplay(consulNodes.filterStatus);
|
37
|
+
consulNodes.displayedCount = 0;
|
38
|
+
consulNodes.servicesStatus = {};
|
39
|
+
var filter = new RegExp(consulNodes.instanceFilter.val());
|
40
|
+
$('#instances-list').children('div').each(function() {
|
41
|
+
var status = $(this).attr('status');
|
42
|
+
var limitNotReached = (consulNodes.displayedCount < consulNodes.maxDisplayed);
|
43
|
+
|
44
|
+
if(nodeMatcher(this, filter)) {
|
45
|
+
if (consulNodes.filterStatus == null || consulNodes.filterStatus == status) {
|
46
|
+
if (limitNotReached) {
|
47
|
+
$(this).removeClass('d-none');
|
48
|
+
$(this).addClass('d-flex');
|
49
|
+
consulNodes.displayedCount++;
|
50
|
+
}
|
51
|
+
var state = this.getAttribute('status');
|
52
|
+
consulNodes.servicesStatus[state] = (consulNodes.servicesStatus[state] || 0) + 1;
|
53
|
+
consulNodes.servicesStatus['total'] = (consulNodes.servicesStatus['total'] || 0) + 1;
|
54
|
+
updateStatusItem(consulNodes.servicesStatus);
|
55
|
+
} else {
|
56
|
+
$(this).removeClass('d-flex');
|
57
|
+
$(this).addClass('d-none');
|
58
|
+
}
|
59
|
+
} else {
|
60
|
+
$(this).removeClass('d-flex');
|
61
|
+
$(this).addClass('d-none');
|
62
|
+
}
|
63
|
+
})
|
64
|
+
}
|
65
|
+
|
66
|
+
displayInstances(instances) {
|
67
|
+
$("#instances-list").html("");
|
68
|
+
|
69
|
+
// var serviceStatus = buildServiceStatus(service);
|
70
|
+
this.displayedCount = 0;
|
71
|
+
|
72
|
+
for (var key in instances) {
|
73
|
+
var instance = instances[key];
|
74
|
+
|
75
|
+
var instanceHtml = document.createElement('div');
|
76
|
+
if(this.displayedCount > this.maxDisplayed) {
|
77
|
+
instanceHtml.setAttribute('class','list-group-item d-none');
|
78
|
+
} else {
|
79
|
+
instanceHtml.setAttribute('class','list-group-item');
|
80
|
+
}
|
81
|
+
|
82
|
+
var sidebar = document.createElement('div');
|
83
|
+
sidebar.setAttribute('class','status-sidebar');
|
84
|
+
var state = getGeneralNodeStatus(instance['Service']);
|
85
|
+
switch(state) {
|
86
|
+
case 'passing': sidebar.classList.add('bg-success'); break;
|
87
|
+
case 'warning': sidebar.classList.add('bg-warning'); break;
|
88
|
+
case 'critical': sidebar.classList.add('bg-danger'); break;
|
89
|
+
}
|
90
|
+
this.servicesStatus[state] = (this.servicesStatus[state] || 0) + 1;
|
91
|
+
this.servicesStatus['total'] = (this.servicesStatus['total'] || 0) + 1;
|
92
|
+
|
93
|
+
var content = document.createElement('div');
|
94
|
+
content.setAttribute('class','instance-content');
|
95
|
+
var contentHead = document.createElement('div');
|
96
|
+
contentHead.setAttribute('class','instance-content-header');
|
97
|
+
contentHead.appendChild(nodeNameGenator(instance['Node']['Name'],instance['Node']['Address']));
|
98
|
+
contentHead.appendChild(nodeAddressGenator(instance['Node']['Address']));
|
99
|
+
contentHead.appendChild(nodeMetaGenator(instance['Node']['Meta']));
|
100
|
+
content.appendChild(contentHead);
|
101
|
+
content.appendChild(servicesGenerator(instance['Service']));
|
102
|
+
content.appendChild(tagsGenerator(getTagsNode(instance)));
|
103
|
+
|
104
|
+
instanceHtml.setAttribute('status', state);
|
105
|
+
instanceHtml.appendChild(sidebar)
|
106
|
+
instanceHtml.appendChild(content)
|
107
|
+
|
108
|
+
$("#instances-list").append(instanceHtml);
|
109
|
+
|
110
|
+
this.displayedCount++;
|
111
|
+
}
|
112
|
+
|
113
|
+
resizeInstances();
|
114
|
+
$('#instances-list .list-group-item').resize(resizeInstances);
|
115
|
+
this.filterInstances();
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
$( window ).resize(resizeInstances);
|
120
|
+
resizeInstances();
|
@@ -5,6 +5,8 @@ class ConsulService {
|
|
5
5
|
this.serviceList = $("#service-list");
|
6
6
|
this.serviceFilter = $("#service-filter");
|
7
7
|
this.serviceFilter.keyup(this.filterService);
|
8
|
+
this.instanceFilter = $("#instance-filter");
|
9
|
+
this.instanceFilter.keyup(this.filterInstances);
|
8
10
|
this.refresh = parseInt(refresh);
|
9
11
|
this.filterStatus = null;
|
10
12
|
this.serviceFilterCounter = $("#service-counter");
|
@@ -18,7 +20,7 @@ class ConsulService {
|
|
18
20
|
}
|
19
21
|
|
20
22
|
fetchRessource() {
|
21
|
-
$.ajax({url:
|
23
|
+
$.ajax({url: this.ressourceURL, cache: false, dataType: "json", sourceObject: this, success: function(result){
|
22
24
|
consulService.initRessource(result);
|
23
25
|
}});
|
24
26
|
}
|
@@ -54,25 +56,45 @@ class ConsulService {
|
|
54
56
|
for (var serviceName in this.data.services) {
|
55
57
|
var service = this.data.services[serviceName];
|
56
58
|
var serviceStatus = buildServiceStatus(service);
|
57
|
-
|
58
|
-
listItem
|
59
|
+
|
60
|
+
var listItem = document.createElement('button');
|
61
|
+
listItem.setAttribute('type','button');
|
62
|
+
listItem.setAttribute('onfocus','consulService.onClickServiceName(this)');
|
63
|
+
listItem.setAttribute('onclick','consulService.onClickServiceName(this)');
|
64
|
+
listItem.setAttribute('value',serviceName);
|
65
|
+
listItem.setAttribute('class','list-group-item list-group-item-action');
|
66
|
+
|
67
|
+
var statuses = document.createElement('div');
|
68
|
+
statuses.setAttribute('class','statuses float-right');
|
69
|
+
|
59
70
|
if (!!serviceStatus['passing']) {
|
60
|
-
|
71
|
+
statuses.appendChild(createBadge('badge-success passing', serviceStatus['passing']));
|
61
72
|
}
|
73
|
+
|
62
74
|
if (!!serviceStatus['warning']) {
|
63
|
-
|
75
|
+
statuses.appendChild(createBadge('badge-warning warning', serviceStatus['warning']));
|
64
76
|
}
|
77
|
+
|
65
78
|
if (!!serviceStatus['critical']) {
|
66
|
-
|
79
|
+
statuses.appendChild(createBadge('badge-danger critical', serviceStatus['critical']));
|
67
80
|
}
|
68
|
-
|
69
|
-
|
70
|
-
listItem
|
81
|
+
|
82
|
+
statuses.appendChild(createBadge('badge-dark', (serviceStatus['total'] || 0)));
|
83
|
+
listItem.appendChild(statuses);
|
84
|
+
|
85
|
+
var serviceNameItem = document.createElement('div');
|
86
|
+
serviceNameItem.setAttribute('class', 'service-name');
|
87
|
+
serviceNameItem.appendChild(document.createTextNode(serviceName));
|
88
|
+
listItem.appendChild(serviceNameItem);
|
89
|
+
|
90
|
+
var serviceTagsItem = document.createElement('div');
|
91
|
+
serviceTagsItem.setAttribute('class', 'service-tags');
|
92
|
+
|
71
93
|
for (var i = 0; i < service.tags.length; i++) {
|
72
|
-
|
94
|
+
serviceTagsItem.appendChild(createBadge('float-right badge-' + (i%2?'secondary':'info') , service.tags[i]));
|
73
95
|
}
|
74
|
-
|
75
|
-
listItem
|
96
|
+
|
97
|
+
listItem.appendChild(serviceTagsItem);
|
76
98
|
this.serviceFilterCount += 1;
|
77
99
|
this.serviceList.append(listItem);
|
78
100
|
}
|
@@ -85,14 +107,13 @@ class ConsulService {
|
|
85
107
|
var filter = new RegExp(consulService.serviceFilter.val());
|
86
108
|
consulService.serviceFilterCount = 0;
|
87
109
|
consulService.serviceList.children('button').each(function (){
|
88
|
-
|
89
|
-
|
110
|
+
var ui = $(this);
|
111
|
+
if(serviceMatcher(this, filter)) {
|
90
112
|
ui.removeClass('d-none');
|
91
113
|
ui.addClass('d-block');
|
92
114
|
consulService.serviceFilterCount += 1;
|
93
115
|
consulService.serviceFilterCounter.html(consulService.serviceFilterCount);
|
94
116
|
} else {
|
95
|
-
var ui = $(this).closest( "button" )
|
96
117
|
ui.removeClass('d-block');
|
97
118
|
ui.addClass('d-none');
|
98
119
|
}
|
@@ -111,24 +132,21 @@ class ConsulService {
|
|
111
132
|
}
|
112
133
|
|
113
134
|
filterInstances() {
|
114
|
-
|
115
|
-
|
116
|
-
if (consulService.filterStatus == null) {
|
117
|
-
$(this).removeClass('progress-deactivated');
|
118
|
-
} else if(consulService.filterStatus == status) {
|
119
|
-
$(this).removeClass('progress-deactivated');
|
120
|
-
} else {
|
121
|
-
$(this).addClass('progress-deactivated');
|
122
|
-
}
|
123
|
-
})
|
135
|
+
updateFilterDisplay(consulService.filterStatus);
|
136
|
+
var filter = new RegExp(consulService.instanceFilter.val());
|
124
137
|
$('#instances-list').children('div').each(function() {
|
125
138
|
var status = $(this).attr('status');
|
126
|
-
if
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
139
|
+
if(instanceMatcher(this, filter)) {
|
140
|
+
if (consulService.filterStatus == null) {
|
141
|
+
$(this).removeClass('d-none');
|
142
|
+
$(this).addClass('d-block');
|
143
|
+
} else if (consulService.filterStatus == status) {
|
144
|
+
$(this).removeClass('d-none');
|
145
|
+
$(this).addClass('d-block');
|
146
|
+
} else {
|
147
|
+
$(this).removeClass('d-block');
|
148
|
+
$(this).addClass('d-none');
|
149
|
+
}
|
132
150
|
} else {
|
133
151
|
$(this).removeClass('d-block');
|
134
152
|
$(this).addClass('d-none');
|
@@ -167,9 +185,9 @@ class ConsulService {
|
|
167
185
|
serviceHtml.setAttribute('class','list-group-item');
|
168
186
|
|
169
187
|
serviceHtml.appendChild(serviceTitleGenerator(instance));
|
170
|
-
serviceHtml.appendChild(tagsGenerator(instance));
|
171
|
-
serviceHtml.appendChild(checksStatusGenerator(instance));
|
172
|
-
var state = nodeState(instance);
|
188
|
+
serviceHtml.appendChild(tagsGenerator(instance.tags));
|
189
|
+
serviceHtml.appendChild(checksStatusGenerator(instance.checks));
|
190
|
+
var state = nodeState(instance.checks);
|
173
191
|
serviceHtml.setAttribute('status', state);
|
174
192
|
$("#instances-list").append(serviceHtml);
|
175
193
|
}
|
@@ -183,153 +201,9 @@ class ConsulService {
|
|
183
201
|
|
184
202
|
resizeWrapper('instances-wrapper', 'instances-list');
|
185
203
|
$('#instances-list .list-group-item').resize(resizeAll);
|
204
|
+
this.filterInstances();
|
186
205
|
}
|
187
206
|
}
|
188
207
|
|
189
|
-
function buildServiceStatus(service) {
|
190
|
-
var serviceStatus = {};
|
191
|
-
|
192
|
-
for (var key in service['instances']) {
|
193
|
-
var instance = service['instances'][key];
|
194
|
-
var state = nodeState(instance);
|
195
|
-
|
196
|
-
serviceStatus[state] = (serviceStatus[state] || 0) + 1;
|
197
|
-
serviceStatus['total'] = (serviceStatus['total'] || 0) + 1;
|
198
|
-
}
|
199
|
-
|
200
|
-
return serviceStatus;
|
201
|
-
}
|
202
|
-
|
203
|
-
function nodeState(instance) {
|
204
|
-
status='passing';
|
205
|
-
for (var checkKey in instance.checks) {
|
206
|
-
switch(instance.checks[checkKey]['status']) {
|
207
|
-
case 'passing': break;
|
208
|
-
case 'warning': status='warning'; break;
|
209
|
-
case 'critical': return 'critical'; break;
|
210
|
-
}
|
211
|
-
}
|
212
|
-
return status;
|
213
|
-
}
|
214
|
-
|
215
|
-
supported_protocols = ['https', 'http', 'sftp', 'ftp', 'ssh', 'telnet']
|
216
|
-
|
217
|
-
function serviceTitleGenerator(instance) {
|
218
|
-
var protocol = null;
|
219
|
-
for (i in supported_protocols) {
|
220
|
-
var protoc = supported_protocols[i]
|
221
|
-
if (instance.tags.includes(protoc)) {
|
222
|
-
protocol = protoc + '://';
|
223
|
-
break;
|
224
|
-
}
|
225
|
-
}
|
226
|
-
|
227
|
-
var htmlTitle = document.createElement('h5');
|
228
|
-
|
229
|
-
var instanceLink = document.createElement('a');
|
230
|
-
if (protocol != null) {
|
231
|
-
instanceLink.setAttribute('href', protocol + instance.addr + ':' + instance.port);
|
232
|
-
instanceLink.setAttribute('target', '_blank');
|
233
|
-
}
|
234
|
-
instanceLink.appendChild(document.createTextNode(instance.name + ':' + instance.port));
|
235
|
-
htmlTitle.appendChild(instanceLink);
|
236
|
-
|
237
|
-
return htmlTitle;
|
238
|
-
}
|
239
|
-
|
240
|
-
function tagsGenerator(instance) {
|
241
|
-
var tags = document.createElement('div');
|
242
|
-
|
243
|
-
tags.className = 'tags';
|
244
|
-
tags.appendChild(document.createTextNode("Tags: "));
|
245
|
-
tags.appendChild(document.createElement('br'));
|
246
|
-
|
247
|
-
for (var tagKey in instance.tags) {
|
248
|
-
var tag = document.createElement('span');
|
249
|
-
tag.setAttribute('class', 'badge badge-secondary mx-1');
|
250
|
-
tag.appendChild(document.createTextNode(instance.tags[tagKey]));
|
251
|
-
tags.appendChild(tag);
|
252
|
-
}
|
253
|
-
return tags;
|
254
|
-
}
|
255
|
-
|
256
|
-
function checksStatusGenerator(instance) {
|
257
|
-
var checks = document.createElement('div');
|
258
|
-
checks.className = 'checks';
|
259
|
-
checks.appendChild(document.createTextNode("Checks: "));
|
260
|
-
checks.appendChild(document.createElement('br'));
|
261
|
-
|
262
|
-
for (var checkKey in instance.checks) {
|
263
|
-
checkId = Math.floor(Math.random()*10000);
|
264
|
-
switch(instance.checks[checkKey]['status']) {
|
265
|
-
case 'passing': var btn = 'btn-success'; break;
|
266
|
-
case 'warning': var btn = 'btn-warning'; break;
|
267
|
-
case 'critical': var btn = 'btn-danger'; break;
|
268
|
-
}
|
269
|
-
var check = document.createElement('div');
|
270
|
-
|
271
|
-
var btnCheck = document.createElement('button');
|
272
|
-
btnCheck.setAttribute('class','btn ' + btn + ' btn-sm m-1');
|
273
|
-
btnCheck.setAttribute('type', 'button');
|
274
|
-
btnCheck.setAttribute('data-toggle', 'collapse');
|
275
|
-
btnCheck.setAttribute('data-target', '#' + checkId);
|
276
|
-
btnCheck.setAttribute('aria-expanded', 'false');
|
277
|
-
|
278
|
-
btnCheck.appendChild(document.createTextNode(instance.checks[checkKey]['name']));
|
279
|
-
|
280
|
-
check.appendChild(btnCheck);
|
281
|
-
|
282
|
-
var collapseCheck = document.createElement('div');
|
283
|
-
collapseCheck.setAttribute('class', 'collapse')
|
284
|
-
collapseCheck.setAttribute('id', checkId)
|
285
|
-
|
286
|
-
var cardCheck = document.createElement('div');
|
287
|
-
cardCheck.setAttribute('class', 'card card-body p-3 m-1 mb-2');
|
288
|
-
|
289
|
-
var notes = document.createElement('table');
|
290
|
-
notes.setAttribute('class', 'table table-hover mb-0');
|
291
|
-
|
292
|
-
var dataToDisplay = ['notes', 'output'];
|
293
|
-
for (var index in dataToDisplay) {
|
294
|
-
var tr = document.createElement('tr');
|
295
|
-
var td1 = document.createElement('td');
|
296
|
-
var td2 = document.createElement('td');
|
297
|
-
var fieldName = dataToDisplay[index].replace(/\b\w/g, l => l.toUpperCase());
|
298
|
-
td1.appendChild(document.createTextNode(fieldName + ': '));
|
299
|
-
var target = td2
|
300
|
-
if (index != 1) {
|
301
|
-
target = document.createElement('div');
|
302
|
-
target.setAttribute("class", "check-notes")
|
303
|
-
} else {
|
304
|
-
// Notes are rendered as plain text
|
305
|
-
target = document.createElement('pre');
|
306
|
-
target.setAttribute("class", "check-output")
|
307
|
-
}
|
308
|
-
target.appendChild(document.createTextNode(instance.checks[checkKey][dataToDisplay[index]]));
|
309
|
-
td2.appendChild(target)
|
310
|
-
tr.appendChild(td1);
|
311
|
-
tr.appendChild(td2);
|
312
|
-
notes.appendChild(tr);
|
313
|
-
}
|
314
|
-
|
315
|
-
cardCheck.appendChild(notes);
|
316
|
-
collapseCheck.appendChild(cardCheck);
|
317
|
-
check.appendChild(collapseCheck);
|
318
|
-
checks.appendChild(check)
|
319
|
-
}
|
320
|
-
|
321
|
-
return checks;
|
322
|
-
}
|
323
|
-
|
324
|
-
function resizeAll() {
|
325
|
-
resizeWrapper('service-wrapper', 'service-list');
|
326
|
-
resizeWrapper('instances-wrapper', 'instances-list');
|
327
|
-
}
|
328
|
-
|
329
|
-
function resizeWrapper(wrapperId, wrapped) {
|
330
|
-
var size = $(window).height() - $('#' + wrapperId).offset()["top"] - 20;
|
331
|
-
$('#' + wrapperId).css("height", size);
|
332
|
-
}
|
333
|
-
|
334
208
|
$( window ).resize(resizeAll);
|
335
209
|
resizeAll();
|