consul-templaterb 1.0.9 → 1.0.10

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: 0d408d6a635426874369831aa3c7f12890ebec2c7b5bb7d571b76bdbef9da041
4
- data.tar.gz: 93af5f9b70790a9c9665bea3d13ae3d6322f7a3b5ff703d132a890cdc37ed6f6
3
+ metadata.gz: 03d2d0aa9f8c3d39ce100425d7b8a2907eea8e0362383a34e41917af4fc8e2b2
4
+ data.tar.gz: e1c0f7dd9bb50d8c10e5be073a00cc52e7de82b0541c8efb973929834b70c48a
5
5
  SHA512:
6
- metadata.gz: 2237575069b92c50374adc5f91587f1737f0cf42a634acb9b469b6cc076175dbdb48e8d944df8de91ccf8b5ebaf089dca4cdb088d4cb46fe07fcb2dbebbabf54
7
- data.tar.gz: 7ad73644600bc5130306c2a9190e092ae59ca08a529e75097b26da91907353a2a50d17fb249a040f8758856917463f035372fb5899728c8ae7f03630b1ee6895
6
+ metadata.gz: 2bf4ff3ce92344d9e79ba00035072753ec95d860bb143f39b0c858038119adf89014e8b3f7e294b4095158840fe43198252da1626a561f49dce796cd94f8549c
7
+ data.tar.gz: d53f88ea95908908aced0200a2afb98d30a156bc4a3f9c0ce66412c1ae6fc4f22f591137bce031fb85e840d7dc7b0116702f891d5a027f44f818e24631b7f116
data/CHANGELOG.md CHANGED
@@ -4,7 +4,14 @@
4
4
 
5
5
  IMPROVEMENTS:
6
6
 
7
- ## 1.0.8 (March 18, 2018)
7
+ # 1.0.10
8
+
9
+ IMPROVEMENTS:
10
+
11
+ * [samples/nodes.html.erb](samples/nodes.html.erb) now also displays the services
12
+ * Added dynamic UI with JSON in directory [samples/consul-ui](samples/consul-ui)
13
+
14
+ ## 1.0.9 (March 20, 2018)
8
15
 
9
16
  IMPROVEMENTS:
10
17
 
data/TemplateAPI.md CHANGED
@@ -14,27 +14,135 @@ thus the application will display a warning if the template is invalid and won't
14
14
 
15
15
  Have a look to [samples/](samples/) directory to start writing your own templates.
16
16
 
17
+ ## Common structure of returned objects
18
+
19
+ All objects returned by those functions described below all share the same structure:
20
+
21
+ * `.result` : handle the result
22
+ * `.endpoint` : get technical information about how data was retrieved and statistics
23
+
24
+ ## Common re-implemented functions for all objects
25
+
26
+ Most objects returned by all those functions are contained within a `.result` object. However, in order
27
+ to avoid having to write .result in all templates, some shortcuts have been added:
28
+ * `[]` allow to either access values for map-based data or arrays
29
+ * for all objects: `.each`, `sort`, `.select`, `.each_value`, `.count`, `.empty?`
30
+ * additionnaly, for map based results, the following methods are available: `.keys`, `.values`, `.each_pair`,
31
+ `.each_value`
32
+
33
+ See [lib/consul/async/consul_template.rb:230](lib/consul/async/consul_template.rb#L230) and
34
+ [lib/consul/async/consul_template.rb:260](lib/consul/async/consul_template.rb#L260) for up to date list of
35
+ all those methods.
36
+
17
37
  ## datacenters()
18
38
 
19
39
  [Get the list of datacenters as string array](https://www.consul.io/api/catalog.html#list-datacenters).
20
40
 
41
+ <details><summary>Examples</summary>
42
+ <div class="samples">
43
+
44
+ ### List all datacenters in a text list and count services and nodes within
45
+
46
+ ```erb
47
+ <% datacenters.each do |dc| %>
48
+ * <%= dc %> with <%= services(dc:dc).keys.count %> services, <%= nodes(dc:dc).count %> nodes
49
+ <% end %>
50
+ ```
51
+
52
+ Full example: [samples/consul_template.txt.erb](samples/consul_template.txt.erb)
53
+
54
+ </div>
55
+ </details>
56
+
21
57
  ## services([dc: datacenter], [tag: tagToFilterWith])
22
58
 
23
59
  [List the services matching the optional tag filter](https://www.consul.io/api/catalog.html#list-services),
24
60
  if tag is not specified, will match all the services. Note that this endpoint performs client side tag
25
61
  filtering for services to ease templates development since this feature is not available on Consul's endpoint.
26
62
 
63
+ <details><summary>Examples</summary>
64
+ <div class="samples">
65
+
66
+ ### List all services in default datacenter and display its tags
67
+
68
+ ```erb
69
+ <% services.each do |service_name, tags|
70
+ %> * <%= service_name %> [ <%= tags %> ]
71
+ <% end %>
72
+ ```
73
+
74
+ Full example: [samples/consul_template.txt.erb](samples/consul_template.txt.erb)
75
+
76
+ ### List all services in all datacenters having tag `http`
77
+
78
+ ```erb
79
+ <%
80
+ datacenters.each do |dc| %>
81
+ * Datacenter <%= dc %>
82
+ <%
83
+ services(dc:dc, tag:'http').each do |service_name, tags|
84
+ %>
85
+ - service <%= service_name %> <%= tags.sort %><%
86
+ end
87
+ end
88
+ %>
89
+ ```
90
+
91
+ </div>
92
+ </details>
93
+
27
94
  ## service(serviceName, [dc: datacenter], [tag: tagToFilterWith], [passing: true])
28
95
 
29
96
  [List the instances](https://www.consul.io/api/health.html#list-nodes-for-service) of a service having the given
30
97
  optional tag. If no tag is specified, will return all instances of service. By default, it will return all the
31
98
  well services that are passing or not. An optional argument passing might be used to retrieve only passing instances.
32
99
 
100
+ <details><summary>Examples</summary>
101
+ <div class="samples">
102
+
103
+ ### List all services instances with http tag on current DC, instances sorted by node name
104
+
105
+ ```erb
106
+ <% services.each do |service_name, tags|
107
+ if tags.include? 'http'
108
+ %> ++ Service <%= service_name %>
109
+ <% service(service_name, tag:'http').sort {|a,b| a['Node']['Node'] <=> b['Node']['Node'] }.each do |snode|
110
+ %> * <%= service_name %> -> <%=
111
+ snode['Node']['Node'] %>:<%= snode['Service']['Port'] %> <%=
112
+ snode['Service']['Tags'] %> status: <%
113
+ snode['Checks'].each do |c| %> <%= c['Status']
114
+ %><% end if snode['Checks'] %>
115
+ <% end
116
+ end
117
+ end %>
118
+ ```
119
+
120
+ Full example: [samples/consul_template.txt.erb](samples/consul_template.txt.erb)
121
+
122
+ </div>
123
+ </details>
124
+
33
125
  ## nodes([dc: datacenter])
34
126
 
35
127
  [List all the nodes of selected datacenter](https://www.consul.io/api/catalog.html#list-nodes). No filtering is
36
128
  applied.
37
129
 
130
+ <details><summary>Examples</summary>
131
+ <div class="samples">
132
+
133
+ ### List all nodes for DC, sorted by name
134
+
135
+ ```erb
136
+ <% nodes.sort {|a,b| a['Node'] <=> b['Node'] }.each do |snode|
137
+ %> * <%= snode['Address'].ljust(16) %> <%= snode['Node'] %>
138
+ <% end %>
139
+ ```
140
+
141
+ Full example: [samples/consul_template.txt.erb](samples/consul_template.txt.erb)
142
+
143
+ </div>
144
+ </details>
145
+
38
146
  ## node(nodeNameOrId, [dc: datacenter])
39
147
 
40
148
  [List all the services of a given Node](https://www.consul.io/api/catalog.html#list-services-for-node) using its
@@ -227,7 +227,7 @@ module Consul
227
227
 
228
228
  class ConsulTemplateAbstract
229
229
  extend Forwardable
230
- def_delegators :result_delegate, :each, :[], :sort, :each_value, :count, :empty?
230
+ def_delegators :result_delegate, :each, :[], :sort, :select, :each_value, :count, :empty?
231
231
  attr_reader :result, :endpoint, :seen_at
232
232
  def initialize(consul_endpoint)
233
233
  @endpoint = consul_endpoint
@@ -1,5 +1,5 @@
1
1
  module Consul
2
2
  module Async
3
- VERSION = '1.0.9'.freeze
3
+ VERSION = '1.0.10'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,38 @@
1
+ <%
2
+ # This template can be configure the following way with environment variables
3
+ # CONSUL_TOOLS_SUFFIX: suffix for the address of consul tools
4
+ # CONSUL_TOOLS_PREFIX: prefix for the address of consul tools
5
+ # CONSUL_TOOLS: comma sperated list of consul tools
6
+ tools = (ENV['CONSUL_TOOLS'] || 'services').split(",")
7
+ suffix = ENV['CONSUL_TOOLS_PREFIX'] || '-ui.html'
8
+ prefix = ENV['CONSUL_TOOLS_SUFFIX'] || 'consul-'
9
+ %><!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="utf-8"/>
13
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
14
+ <meta name="description" content="Display Consul information"/>
15
+ <meta name="author" content="Criteo"/>
16
+ <meta http-equiv="refresh" content="<%= param('refresh', ENV['REFRESH'] || '600') %>"/>
17
+ <title><%= param('title', 'Consul Real Time information') %></title>
18
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
19
+ <!-- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> -->
20
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.11/css/all.css" integrity="sha384-p2jx59pefphTFIpeqCcISO9MdVfIm4pNnsL08A6v5vaQc4owkQqxMV8kg4Yvhaw/" crossorigin="anonymous">
21
+ <link rel="stylesheet" href="css/style.css">
22
+ </head>
23
+ <body>
24
+ <nav class="navbar navbar-expand-md navbar-dark bg-secondary">
25
+ <a class="navbar-brand" href="#">Consul</a>
26
+ <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
27
+ <span class="navbar-brand">Consul</span>
28
+ </button>
29
+ <div class="collapse navbar-collapse" id="navbarsExampleDefault">
30
+ <ul class="navbar-nav mr-auto">
31
+ <% tools.each do |tool| %>
32
+ <li class="nav-item">
33
+ <a class="nav-link" href="<%= prefix + tool + suffix %>"><%= tool.gsub('_', ' ').capitalize %></a>
34
+ </li>
35
+ <% end %>
36
+ </ul>
37
+ </div>
38
+ </nav>
@@ -0,0 +1,31 @@
1
+ <% datasource = ENV['SERVICE_DATASOURCE'] || 'consul-template.json' %>
2
+ <%= render_file('common/header.html.erb', title: 'Services') %>
3
+ <div class="main">
4
+ <div class="row mx-0">
5
+ <div id="filter-menu" class="col-4 col-m-3 px-4 pt-4">
6
+ <div class="form-group">
7
+ <input id="service-filter" type="text" placeholder="filter" class="form-control" >
8
+ </div>
9
+ <div id="service-wrapper" >
10
+ <ul id="service-list" class="list-group">
11
+ </ul>
12
+ </div>
13
+ </div>
14
+ <div class="col-8 col-m-9">
15
+ <h2 class="text-center" id="service-title"></h2>
16
+ <div id="instances-wrapper">
17
+ <div id="instances-list" class="list-group"></div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ <!-- Optional JavaScript -->
23
+ <!-- JavaScript Dependencies: jQuery, Popper.js, Bootstrap JS, Shards JS -->
24
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
25
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
26
+ <script src="js/service.js"></script>
27
+ <script type="text/javascript">
28
+ consulService = new ConsulService('<%= datasource %>');
29
+ </script>
30
+ </body>
31
+ </html>
@@ -0,0 +1,89 @@
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'] || 'http'
10
+ instance_must_tag = ENV['INSTANCE_MUST_TAG'] || service_tag_filter
11
+ instance_exclude_tag = ENV['INSTANCE_EXCLUDE_TAG'] || 'canary'
12
+
13
+ # Services to hide
14
+ services_blacklist = (ENV['EXCLUDE_SERVICES'] || 'consul-agent-http,mesos-slave,mesos-agent-watcher,mesos-exporter-slave').split(',')
15
+ # Compute the health of a Service
16
+ def compute_state(snode)
17
+ states = ['passing', []]
18
+ snode['Checks'].each do |chk|
19
+ st = chk['Status']
20
+ states[1] << st
21
+ if st == 'critical'
22
+ states[0] = st
23
+ elsif st == 'warning' && states[0] == 'passing'
24
+ states[0] = st
25
+ end
26
+ end
27
+ states
28
+ end
29
+ def compute_attributes(snode)
30
+ w = 100
31
+ snode['Service']['Tags'].each do |tag|
32
+ match = /^weight-([1-9][0-9])*$/.match(tag)
33
+ w = match[1].to_i if match
34
+ end
35
+ attributes = ""
36
+ states = compute_state(snode)
37
+ attributes = "#{attributes} disabled" if states[0] == 'critical'
38
+ if states[0] == 'warning'
39
+ w = w / 8
40
+ end
41
+ attributes = "#{attributes} weight #{w}" if w.positive?
42
+ end
43
+ backends = {}
44
+ services(tag: service_tag_filter).each do |service_name, tags|
45
+ if !services_blacklist.include?(service_name) && tags.include?(instance_must_tag)
46
+ the_backends = []
47
+ service(service_name).sort {|a,b| a['Node']['Node'] <=> b['Node']['Node'] }.each do |snode|
48
+ tags_of_instance = snode['Service']['Tags']
49
+ if tags_of_instance.include?(instance_must_tag) && !tags_of_instance.include?(instance_exclude_tag)
50
+ the_backends << snode if snode['Service']['Port']
51
+ end
52
+ end
53
+ # We add the backend ONLY if at least one valid instance does exists
54
+ backends[service_name] = the_backends
55
+ end
56
+ end
57
+ %><%
58
+ json_backends = {}
59
+ backends.each_pair do |service_name, nodes|
60
+ service = {
61
+ name: service_name,
62
+ count: nodes.count,
63
+ instances: []
64
+ }
65
+ json_backends[service_name] = service
66
+ nodes.each do |snode|
67
+ checks = []
68
+ snode['Checks'].each do |ncheck|
69
+ check = {}
70
+ check['checkid'] = ncheck['CheckID']
71
+ check['name'] = ncheck['Name']
72
+ check['output'] = ncheck['Output']
73
+ check['status'] = ncheck['Status']
74
+ check['notes'] = ncheck['Notes']
75
+ checks.push(check)
76
+ end
77
+ server = { frontend_id: "backend_http__#{service_name}",
78
+ id: snode['Service']['ID'],
79
+ name: snode['Node']['Node'],
80
+ addr: snode['Node']['Address'],
81
+ port: snode['Service']['Port'],
82
+ tags: snode['Service']['Tags'],
83
+ checks: checks,
84
+ }
85
+ service[:instances] << server
86
+ end
87
+ end
88
+ json = { services: json_backends}
89
+ %><%= JSON.pretty_generate(json) %>
@@ -0,0 +1,57 @@
1
+ .btn:focus, .btn:active, button {
2
+ outline: none !important;
3
+ box-shadow: none;
4
+ }
5
+
6
+ #service-wrapper, #instances-wrapper {
7
+ overflow: scroll;
8
+ border-top-left-radius: 0px;
9
+ border-top-right-radius: 0px;
10
+ border-style: solid;
11
+ border-width: 1px 1px 1px 1px;
12
+ border-color: rgba(0,0,0,.125);
13
+ }
14
+
15
+ #service-wrapper .list-group-item:last-child, #instances-wrapper .list-group-item:last-child {
16
+ border-bottom-width: 0px;
17
+ border-bottom-left-radius: 0px;
18
+ border-bottom-right-radius: 0px;
19
+ }
20
+
21
+ #service-wrapper .list-group-item:first-child, #instances-wrapper .list-group-item:first-child {
22
+ border-top-width: 0px;
23
+ border-top-left-radius: 0px;
24
+ border-top-right-radius: 0px;
25
+ }
26
+
27
+ #service-wrapper .list-group-item, #instances-wrapper .list-group-item {
28
+ border-left: 0px;
29
+ border-right: 0px;
30
+ }
31
+
32
+ h2 {
33
+ margin-top: 1rem;
34
+ margin-bottom: 1rem;
35
+ }
36
+
37
+ h2 .fas {
38
+ cursor: pointer;
39
+ color: rgba( 0, 0, 0, 0.5);
40
+ font-size: 1.5rem;
41
+ transition: color .16s linear
42
+ }
43
+
44
+ h2 .fas:hover {
45
+ cursor: pointer;
46
+ color: #007bff;
47
+ opacity: 1;
48
+ font-size: 1.5rem;
49
+ }
50
+
51
+ .custom-links {
52
+ background-color: #EEEEEE;
53
+ color: #333333;
54
+ margin: 3px;
55
+ padding: 5px;
56
+ padding-left: 10px;
57
+ }
@@ -0,0 +1,206 @@
1
+ class ConsulService {
2
+ constructor(ressourceURL) {
3
+ this.ressourceURL = ressourceURL;
4
+ this.fetchRessource();
5
+ this.serviceList = $("#service-list");
6
+ this.serviceFilter = $("#service-filter");
7
+ this.serviceFilter.keyup(this.filterService)
8
+ }
9
+
10
+ fetchRessource() {
11
+ $.ajax({url: "consul_template.json", cache: false, dataType: "json", sourceObject: this, success: function(result){
12
+ if(this.sourceObject.data) {
13
+ // For autoupdate
14
+ } else {
15
+ this.sourceObject.initRessource(result);
16
+ }
17
+ }});
18
+ }
19
+
20
+ initRessource(data) {
21
+ this.data = data;
22
+ this.reloadServiceList();
23
+ var urlParam = new URL(location.href).searchParams.get('service')
24
+ if(urlParam) {
25
+ var nodes = document.getElementById('service-list').childNodes;
26
+ for(var i in nodes) {
27
+ if($(nodes[i]).html() == urlParam) {
28
+ this.selectService(nodes[i]);
29
+ break;
30
+ }
31
+ }
32
+ } else {
33
+ this.selectService(document.getElementById('service-list').firstElementChild);
34
+ }
35
+ }
36
+
37
+ reloadServiceList() {
38
+ for (var service in this.data.services) {
39
+ var listItem = '<button type="button" onclick="consulService.onClickServiceName(this)" class="list-group-item list-group-item-action">';
40
+ listItem += service;
41
+ listItem += '</button>';
42
+ this.serviceList.append(listItem);
43
+ }
44
+ resizeWrapper('service-wrapper', 'service-list');
45
+ }
46
+
47
+ filterService(e) {
48
+ var filter = new RegExp(e.target.value);
49
+ consulService.serviceList.children('button').each(function (){
50
+ if($(this).html().match(filter)) {
51
+ $(this).removeClass('d-none');
52
+ $(this).addClass('d-block');
53
+ } else {
54
+ $(this).removeClass('d-block');
55
+ $(this).addClass('d-none');
56
+ }
57
+ })
58
+ }
59
+
60
+ onClickServiceName(source) {
61
+ this.selectService(source);
62
+ this.updateURL();
63
+ }
64
+
65
+ updateURL() {
66
+ var newUrl = window.location.protocol + "//" + window.location.host + window.location.pathname;
67
+ newUrl += '?service=' + $(this.selectedService).html();
68
+ window.history.pushState({},"",newUrl);
69
+ }
70
+
71
+ selectService(source) {
72
+ if (this.selectedService) {
73
+ $(this.selectedService).removeClass('active');
74
+ }
75
+ this.selectedService = source;
76
+ $(this.selectedService).addClass('active');
77
+
78
+ var serviceName = $(source).html();
79
+
80
+ this.displayService(this.data.services[serviceName]);
81
+ }
82
+
83
+ displayService(service) {
84
+ $("#service-title").html(service['name']);
85
+ $("#instances-list").html("")
86
+
87
+ for (var key in service['instances']) {
88
+ var instance = service['instances'][key];
89
+ var serviceHtml = document.createElement('div');
90
+ serviceHtml.setAttribute('class','list-group-item');
91
+
92
+ serviceHtml.appendChild(serviceTitleGenerator(instance));
93
+ serviceHtml.appendChild(tagsGenerator(instance));
94
+ serviceHtml.appendChild(checksStatusGenerator(instance));
95
+
96
+ $("#instances-list").append(serviceHtml);
97
+ }
98
+
99
+ resizeWrapper('instances-wrapper', 'instances-list');
100
+ $('#instances-list .list-group-item').resize(resizeAll);
101
+ }
102
+ }
103
+
104
+ function serviceTitleGenerator(instance) {
105
+ var protocol = 'http://';
106
+ if(instance.tags.includes('https')) {
107
+ protocol = 'https://';
108
+ }
109
+
110
+ var htmlTitle = document.createElement('h5');
111
+
112
+ var instanceLink = document.createElement('a');
113
+ instanceLink.setAttribute('href', protocol + instance.name + ':' + instance.port);
114
+ instanceLink.setAttribute('target', '_blank');
115
+ instanceLink.appendChild(document.createTextNode(instance.name + ':' + instance.port));
116
+ htmlTitle.appendChild(instanceLink);
117
+
118
+ return htmlTitle;
119
+ }
120
+
121
+ function tagsGenerator(instance) {
122
+ var tags = document.createElement('div');
123
+
124
+ tags.className = 'tags';
125
+ tags.appendChild(document.createTextNode("Tags: "));
126
+ tags.appendChild(document.createElement('br'));
127
+
128
+ for (var tagKey in instance.tags) {
129
+ var tag = document.createElement('span');
130
+ tag.setAttribute('class', 'badge badge-secondary mx-1');
131
+ tag.appendChild(document.createTextNode(instance.tags[tagKey]));
132
+ tags.appendChild(tag);
133
+ }
134
+ return tags;
135
+ }
136
+
137
+ function checksStatusGenerator(instance) {
138
+ var checks = document.createElement('div');
139
+ checks.className = 'checks';
140
+ checks.appendChild(document.createTextNode("Checks: "));
141
+ checks.appendChild(document.createElement('br'));
142
+
143
+ for (var checkKey in instance.checks) {
144
+ checkId = Math.floor(Math.random()*10000);
145
+ switch(instance.checks[checkKey]['status']) {
146
+ case 'passing': var btn = 'btn-success'; break;
147
+ case 'warning': var btn = 'btn-warning'; break;
148
+ case 'critical': var btn = 'btn-danger'; break;
149
+ }
150
+ var check = document.createElement('div');
151
+
152
+ var btnCheck = document.createElement('button');
153
+ btnCheck.setAttribute('class','btn ' + btn + ' btn-sm m-1');
154
+ btnCheck.setAttribute('type', 'button');
155
+ btnCheck.setAttribute('data-toggle', 'collapse');
156
+ btnCheck.setAttribute('data-target', '#' + checkId);
157
+ btnCheck.setAttribute('aria-expanded', 'false');
158
+
159
+ btnCheck.appendChild(document.createTextNode(instance.checks[checkKey]['name']));
160
+
161
+ check.appendChild(btnCheck);
162
+
163
+ var collapseCheck = document.createElement('div');
164
+ collapseCheck.setAttribute('class', 'collapse')
165
+ collapseCheck.setAttribute('id', checkId)
166
+
167
+ var cardCheck = document.createElement('div');
168
+ cardCheck.setAttribute('class', 'card card-body p-3 m-1 mb-2');
169
+
170
+ var notes = document.createElement('table');
171
+ notes.setAttribute('class', 'table table-hover mb-0');
172
+
173
+ var dataToDisplay = ['notes', 'output'];
174
+ for (var index in dataToDisplay) {
175
+ var tr = document.createElement('tr');
176
+ var td1 = document.createElement('td');
177
+ var td2 = document.createElement('td');
178
+ var fieldName = dataToDisplay[index].replace(/\b\w/g, l => l.toUpperCase());
179
+ td1.appendChild(document.createTextNode(fieldName + ': '));
180
+ td2.appendChild(document.createTextNode(instance.checks[checkKey][dataToDisplay[index]]));
181
+ tr.appendChild(td1);
182
+ tr.appendChild(td2);
183
+ notes.appendChild(tr);
184
+ }
185
+
186
+ cardCheck.appendChild(notes);
187
+ collapseCheck.appendChild(cardCheck);
188
+ check.appendChild(collapseCheck);
189
+ checks.appendChild(check)
190
+ }
191
+
192
+ return checks;
193
+ }
194
+
195
+ function resizeAll() {
196
+ resizeWrapper('service-wrapper', 'service-list');
197
+ resizeWrapper('instances-wrapper', 'instances-list');
198
+ }
199
+
200
+ function resizeWrapper(wrapperId, wrapped) {
201
+ var size = $(window).height() - $('#' + wrapperId).offset()["top"] - 20;
202
+ $('#' + wrapperId).css("height", size);
203
+ }
204
+
205
+ $( window ).resize(resizeAll);
206
+ resizeAll();
@@ -1,14 +1,45 @@
1
- <%= render_file('common/header.html.erb', title: 'Nodes') %>
1
+ <%= render_file('common/header.html.erb', title: 'Nodes') %><%
2
+ service_per_node = {}
3
+ services.each do |service_name, tags|
4
+ service(service_name, tag:'http').sort {|a,b| a['Node']['Node'] <=> b['Node']['Node'] }.each do |snode|
5
+ node_services = service_per_node[snode['Node']['Node']] || []
6
+ node_services.push(snode)
7
+ service_per_node[snode['Node']['Node']] = node_services
8
+ end
9
+ end %>
10
+
2
11
  <h1 id="nodes">List all nodes for DC, sorted by name</h1>
3
- <ul>
12
+ <ul class="list-group">
4
13
  <% nodes.sort {|a,b| a['Node'] <=> b['Node'] }.each do |snode|
5
- %><li id="node_<%= snode['ID'] %>"><a href="ssh://<%= snode['Address']%>"><%= snode['Address'] %></a> <%= snode['Node'] %><%
14
+ %><li id="node_<%= snode['ID'] %>" class="list-group-item">
15
+ <a href="ssh://<%= snode['Address']%>"><%= snode['Address'] %></a> <%= snode['Node'] %><%
6
16
  snode['Meta'].each do |k,v|
7
17
  if v && !v.empty?
8
18
  %><span class="badge badge-pill badge-primary float-right"><%= k %>:&nbsp;<%= v%></span><%
9
19
  end
10
20
  end if snode['Meta']
11
- %></li>
21
+ %><div><%
22
+ if service_per_node.key?(snode['Node'])
23
+ service_per_node[snode['Node']].each do |service|
24
+ tags = service['Service']['Tags'].sort
25
+ addr = service['Node']['Address']
26
+ port_num = service['Service']['Port'].to_i
27
+ port = port_num && port_num > 0 ? ":#{port_num}" : ''
28
+ url = if tags.include? 'https'
29
+ "https://#{addr}#{port}"
30
+ elsif tags.include? 'http'
31
+ "http://#{addr}#{port}"
32
+ elsif tags.include? 'ftp'
33
+ "ftp://#{addr}#{port}"
34
+ else
35
+ nil
36
+ end
37
+ %> <a class="badge badge-secondary" href='<%= url %>'>
38
+ <%= service['Service']['Service'] %>: <%= port_num %>
39
+ </a>
40
+ <% end
41
+ end
42
+ %></div></li>
12
43
  <% end %>
13
44
  </ul>
14
- <%= render_file 'common/footer.html.erb' %>
45
+ <%= render_file 'common/footer.html.erb' %>
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.0.9
4
+ version: 1.0.10
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-03-20 00:00:00.000000000 Z
11
+ date: 2018-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request
@@ -158,6 +158,11 @@ files:
158
158
  - samples/checks.html.erb
159
159
  - samples/common/footer.html.erb
160
160
  - samples/common/header.html.erb
161
+ - samples/consul-ui/common/header.html.erb
162
+ - samples/consul-ui/consul-services-ui.html.erb
163
+ - samples/consul-ui/consul_template.json.erb
164
+ - samples/consul-ui/css/style.css
165
+ - samples/consul-ui/js/service.js
161
166
  - samples/consul_template.html.erb
162
167
  - samples/consul_template.json.erb
163
168
  - samples/consul_template.txt.erb