killbill-kpm-ui 1.0.2 → 2.0.0

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
- SHA1:
3
- metadata.gz: 2d36d5716f6a5803657904f97c6f2874f266af1e
4
- data.tar.gz: 5d0eefbbfe86ed48efd6eeec07575a47de3ddad7
2
+ SHA256:
3
+ metadata.gz: fdbc04b26c436283f357622893a3ee362a964b7f921c8392537bd31f4b75fe5f
4
+ data.tar.gz: '058b708bbdfb03150fc0e5ea0defbc06e85208307d2d03928e27790c0e8eb66c'
5
5
  SHA512:
6
- metadata.gz: 8164906e17512ba0fb89b4447ae929da383f276a361d1922854853871707f6332cdfd6cb4f7344d7dc67c96238efe2bec947ce961df9a43d3baec09121940b23
7
- data.tar.gz: fbf349a46acf0eccf9386830bdc45f36df1058953875a4e271aaf4a2b27b4bfd329627d107c4b69f8d4f434fa6ccad80133aa9b137188e59d3dac04d2e9cf877
6
+ metadata.gz: dd2d30639edf9d039b90831f57ae44f971c0c17477958f8d8ffa5e97701bfe35bc4cd61d1089440900b1efdd84e9dc9386f995df2b984e96ad7491060519c24b
7
+ data.tar.gz: d8853cd0a36a042731d3a2673a094b3169ee901d01b6e9a68eff80c27436621c9b8c024f1b61ccc66628ecc0bcdf1e4ca7ef02f0e78a8af442391a83b566b0b6
data/README.md CHANGED
@@ -13,7 +13,8 @@ Kill Bill compatibility
13
13
  | 0.1.y | 0.16.z |
14
14
  | 0.2.y | 0.18.z (Rails 4) |
15
15
  | 0.3.y | 0.18.z (Rails 5) |
16
- | 1.x.y | 0.19.z |
16
+ | 1.x.y | 0.20.z (Rails 5) |
17
+ | 2.x.y | 0.22.z |
17
18
 
18
19
  Testing
19
20
  -------
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -14,14 +16,11 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
16
  rdoc.rdoc_files.include('lib/**/*.rb')
15
17
  end
16
18
 
17
- APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
19
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
18
20
  load 'rails/tasks/engine.rake'
19
21
 
20
-
21
22
  load 'rails/tasks/statistics.rake'
22
23
 
23
-
24
-
25
24
  require 'bundler/gem_tasks'
26
25
 
27
26
  require 'rake/testtask'
@@ -33,5 +32,4 @@ Rake::TestTask.new(:test) do |t|
33
32
  t.verbose = false
34
33
  end
35
34
 
36
-
37
35
  task default: :test
@@ -0,0 +1,48 @@
1
+ function refreshLogLine(log) {
2
+ var table = window.logsDataTable;
3
+ if (table === null) {
4
+ return;
5
+ }
6
+
7
+ var levelLabel = 'success';
8
+ if (log['level'] === 'ERROR') {
9
+ levelLabel = 'danger';
10
+ } else if (log['level'] === 'WARNING') {
11
+ levelLabel = 'warning';
12
+ }
13
+
14
+ var newDate = new Date();
15
+ newDate.setTime(log['time']);
16
+ var newRow = $('<tr>')
17
+ .append($('<td>').text(newDate.toISOString()))
18
+ .append($('<td>')
19
+ .append($('<span>')
20
+ .attr('class', 'label label-' + levelLabel)
21
+ .text(log['level'])
22
+ )
23
+ )
24
+ .append($('<td>')
25
+ .append($('<pre>')
26
+ .text(log['message'])
27
+ )
28
+ );
29
+
30
+ // Enforce ordering on refresh to remove oldest entry (expected at the bottom)
31
+ table.order([0, 'desc']).draw();
32
+
33
+ // length needs to match pageLength in initializer
34
+ if (table.column(0).data().length >= 100) {
35
+ table.row($('tr:last')).remove();
36
+ }
37
+
38
+ table.rows.add(newRow).draw();
39
+ }
40
+
41
+ function refreshLogs(url) {
42
+ var source = new EventSource(url);
43
+ source.addEventListener('refresh', function (e) {
44
+ if (e['data'] !== 'heartbeat') {
45
+ refreshLogLine(JSON.parse(e.data));
46
+ }
47
+ });
48
+ }
@@ -0,0 +1,23 @@
1
+ #nodes-table th:first-of-type,
2
+ #nodes-table td:first-of-type,
3
+ #logs-table th:first-of-type,
4
+ #logs-table td:first-of-type {
5
+ padding-left: 8px;
6
+ }
7
+
8
+ #nodes-table ul {
9
+ list-style-type: none;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ #logs-table pre {
15
+ white-space: pre-wrap; /* css-3 */
16
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
17
+ white-space: -o-pre-wrap; /* Opera 7 */
18
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
19
+ }
20
+
21
+ #logs-form {
22
+ margin-bottom: 15px;
23
+ }
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KPM
2
4
  class EngineController < ApplicationController
3
-
4
5
  layout :get_layout
5
6
 
6
7
  def get_layout
@@ -17,13 +18,12 @@ module KPM
17
18
  def options_for_klient
18
19
  user = current_tenant_user
19
20
  {
20
- :username => user[:username],
21
- :password => user[:password],
22
- :session_id => user[:session_id],
23
- :api_key => user[:api_key],
24
- :api_secret => user[:api_secret]
21
+ :username => user[:username],
22
+ :password => user[:password],
23
+ :session_id => user[:session_id],
24
+ :api_key => user[:api_key],
25
+ :api_secret => user[:api_secret]
25
26
  }
26
27
  end
27
-
28
28
  end
29
29
  end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kpm/client'
2
4
 
3
5
  module KPM
4
6
  class NodesInfoController < EngineController
7
+ helper :all
8
+ include ActionController::Live
5
9
 
6
10
  def index
7
11
  @nodes_info = ::KillBillClient::Model::NodesInfo.nodes_info(options_for_klient)
@@ -9,6 +13,7 @@ module KPM
9
13
  # For convenience, put pure OSGI bundles at the bottom
10
14
  @nodes_info.each do |node_info|
11
15
  next if node_info.plugins_info.nil?
16
+
12
17
  node_info.plugins_info.sort! do |a, b|
13
18
  if osgi_bundle?(a) && !osgi_bundle?(b)
14
19
  1
@@ -20,31 +25,57 @@ module KPM
20
25
  end
21
26
  end
22
27
 
23
- @logs = ::Killbill::KPM::KPMClient.get_osgi_logs(options_for_klient).reverse
28
+ @kb_host = params[:kb_host] || KillBillClient::API.base_uri
29
+ @last_event_id = params[:last_event_id]
30
+ end
24
31
 
25
- respond_to do |format|
26
- format.html
27
- format.js
32
+ def refresh
33
+ response.headers['Content-Type'] = 'text/event-stream'
34
+
35
+ last_event_id_ref = Concurrent::AtomicReference.new(request.headers['Last-Event-Id'] || params[:last_event_id])
36
+ sse = nil
37
+ sse_client = nil
38
+ begin
39
+ # Kaui -> Browser
40
+ sse = ActionController::Live::SSE.new(response.stream, :retry => 300, :event => 'refresh')
41
+
42
+ # Kill Bill -> Kaui
43
+ sse_client = ::Killbill::KPM::KPMClient.stream_osgi_logs(sse, params[:kb_host], last_event_id_ref)
44
+
45
+ i = 0
46
+ # We force the browser to reconnect periodically (ensures clients don't block the server shutdown sequence)
47
+ while i < 6 # 30s
48
+ i += 1
49
+ # Keep the thread alive (Kill Bill should send us a heartbeat as well though)
50
+ # Note that we set the id as the last log id, so that we can easily resume
51
+ sse.write('heartbeat', :id => last_event_id_ref.get)
52
+ sleep 5
53
+ end
54
+ rescue ActionController::Live::ClientDisconnected
55
+ # ignored
56
+ ensure
57
+ begin
58
+ begin
59
+ sse_client.close unless sse_client.nil?
60
+ rescue StandardError
61
+ # ignored
62
+ end
63
+ sse.close unless sse.nil?
64
+ ensure
65
+ # Clear dead DB connections
66
+ # Very lame, but I couldn't do better... Rails will checkout a DB connection
67
+ # whenever a new Thread is spawn and ActiveRecord::Base.clear_active_connections!
68
+ # didn't seem to do the trick: the number of active and dead connections kept growing:
69
+ # connections = ActiveRecord::Base.connection_pool.instance_eval { @connections }
70
+ # busy = connections.count { |c| c.in_use? }
71
+ # dead = connections.count { |c| c.in_use? && !c.owner.alive? }
72
+ ActiveRecord::Base.connection_pool.reap
73
+ end
28
74
  end
29
75
  end
30
76
 
31
77
  def install_plugin
32
- command_properties = [
33
- build_node_command_property('forceDownload', params[:force_download] == '1')
34
- ]
35
- trigger_node_plugin_command('INSTALL_PLUGIN', command_properties)
36
-
37
- redirect_to :action => :index
38
- end
39
-
40
- def install_plugin_from_fs
41
- ::Killbill::KPM::KPMClient.install_plugin(params.require(:key),
42
- params.require(:version),
43
- params.require(:type),
44
- params.require(:plugin).original_filename,
45
- params.require(:plugin).read,
46
- options_for_klient)
47
-
78
+ trigger_node_plugin_command('INSTALL_PLUGIN')
48
79
  redirect_to :action => :index
49
80
  end
50
81
 
@@ -70,21 +101,25 @@ module KPM
70
101
 
71
102
  private
72
103
 
73
- def trigger_node_plugin_command(command_type, command_properties=[])
104
+ def trigger_node_plugin_command(command_type, command_properties = [])
105
+ # No need to pass kbVersion -- Kill Bill will figure it out
74
106
  command_properties << build_node_command_property('pluginKey', params[:plugin_key])
75
107
  command_properties << build_node_command_property('pluginName', params[:plugin_name])
76
108
  command_properties << build_node_command_property('pluginVersion', params[:plugin_version])
109
+ command_properties << build_node_command_property('pluginType', params[:plugin_type])
110
+ command_properties << build_node_command_property('pluginUri', params[:plugin_uri])
111
+ command_properties << build_node_command_property('forceDownload', params[:force_download] == '1')
77
112
 
78
113
  trigger_node_command(command_type, command_properties)
79
114
  end
80
115
 
81
- def trigger_node_command(command_type, command_properties=[])
116
+ def trigger_node_command(command_type, command_properties = [])
82
117
  node_command = ::KillBillClient::Model::NodeCommandAttributes.new
83
118
  node_command.is_system_command_type = true
84
119
  node_command.node_command_type = command_type
85
120
  node_command.node_command_properties = command_properties
86
121
 
87
- # TODO Can we actually use node_name?
122
+ # TODO: Can we actually use node_name?
88
123
  local_node_only = false
89
124
 
90
125
  ::KillBillClient::Model::NodesInfo.trigger_node_command(node_command,
@@ -1,25 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kpm/client'
2
4
 
3
5
  module KPM
4
6
  class PluginsController < EngineController
5
-
6
7
  def index
7
8
  nodes_by_kb_version, @kb_version = killbill_version
8
9
  @warning_message = ''
9
- plugins = []
10
- if nodes_by_kb_version.size > 1
10
+ @plugins = {}
11
+ if !nodes_by_kb_version.nil? && nodes_by_kb_version.size > 1
11
12
  @warning_message = different_versions_warning_message(nodes_by_kb_version)
12
13
  else
14
+ full_kb_version = nodes_by_kb_version.nil? ? 'LATEST' : nodes_by_kb_version.keys.first
13
15
  begin
14
- plugins = ::Killbill::KPM::KPMClient.get_available_plugins(true, options_for_klient)
15
- rescue => e
16
- # No connectivity, GitHub down, ...
17
- Rails.logger.warn("Unable to get latest plugins, trying built-in directory: #{e.inspect}")
18
- plugins = ::Killbill::KPM::KPMClient.get_available_plugins(false, options_for_klient)
16
+ plugins_metadata = ::Killbill::KPM::KPMClient.get_available_plugins(full_kb_version, true, options_for_klient)
17
+ rescue StandardError => e
18
+ # No connectivity or version not in Nexus
19
+ Rails.logger.warn("Unable to get latest plugins for version #{full_kb_version}: #{e.inspect}")
20
+ plugins_metadata = ::Killbill::KPM::KPMClient.get_available_plugins('LATEST', false, options_for_klient)
19
21
  end
20
- plugins.select! { |plugin_key, info| info['versions'].keys.include?(@kb_version) }
22
+ @plugins = Hash[plugins_metadata['plugins'].sort]
21
23
  end
22
- @plugins = Hash[plugins.sort]
23
24
  end
24
25
 
25
26
  private
@@ -43,6 +44,5 @@ module KPM
43
44
  end
44
45
  [nodes_by_kb_version, first_node_version.scan(/(\d+\.\d+)(\.\d)?/).flatten[0]]
45
46
  end
46
-
47
47
  end
48
48
  end
@@ -1,4 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KPM
2
4
  module ApplicationHelper
5
+ # Return true if the KPM plugin is on at least one node
6
+ # (INSTALL_PLUGIN and UNINSTALL_PLUGIN are handled by KPM plugin, not by the core)
7
+ def kpm_plugin_installed?(nodes_info)
8
+ nodes_info.each do |node_info|
9
+ next if (node_info.plugins_info || []).empty?
10
+
11
+ node_info.plugins_info.each do |plugin_info|
12
+ return true if plugin_info.plugin_name == 'org.kill-bill.billing.killbill-platform-osgi-bundles-kpm'
13
+ end
14
+ end
15
+ false
16
+ end
3
17
  end
4
18
  end
@@ -2,42 +2,39 @@
2
2
  <thead>
3
3
  <tr>
4
4
  <th>Time</th>
5
- <th>Name</th>
6
5
  <th>Level</th>
7
6
  <th>Message</th>
8
7
  </tr>
9
8
  </thead>
10
9
  <tbody>
11
- <% logs.each do |log| %>
12
- <tr>
13
- <td>
14
- <% unless log['time'].blank? %>
15
- <%= DateTime.strptime(log['time'].to_s, '%Q').strftime('%Y-%m-%d %H:%M:%S') %>
16
- <% end %>
17
- </td>
18
- <td><%= log['name'] %></td>
19
- <td>
20
- <% if log['level'] == 'ERROR' %>
21
- <span class="label label-danger"><%= log['level'] %></span>
22
- <% elsif log['level'] == 'WARNING' %>
23
- <span class="label label-warning"><%= log['level'] %></span>
24
- <% else %>
25
- <span class="label label-success"><%= log['level'] %></span>
26
- <% end %>
27
- </td>
28
- <td><pre><%= log['message'] %></pre></td>
29
- </tr>
30
- <% end %>
31
10
  </tbody>
32
11
  </table>
33
12
 
13
+ <%= form_tag url_for, :method => 'get', :format => :js, :id => 'kb_host-form', :class => 'form-horizontal', :style => 'display: none;' do %>
14
+ <div id="logs-form">
15
+ <div class="form-inline">
16
+ <%= text_field_tag :kb_host, kb_host, :class => 'form-control' %>
17
+ <%= button_tag '', :type => 'submit', :class => 'glyphicon glyphicon-search' %>
18
+ </div>
19
+ </div>
20
+ <% end %>
21
+
34
22
  <%= javascript_tag do %>
35
23
  $(document).ready(function() {
36
- $('#logs-table').dataTable({
37
- "dom": "<'row'r>t<'row'<'col-md-6'i><'col-md-6'p>>",
38
- "pagingType": "full_numbers",
39
- "pageLength": 50,
40
- "ordering": false
24
+ window.logsDataTable = $('#logs-table').DataTable({
25
+ "columns": [
26
+ { "width": "165px" }, /* ISO String is constant in width */
27
+ { "width": "60px" }, /* Log levels are bounded in width */
28
+ null,
29
+ ],
30
+ "dom": "<'row'<'col-md-6'><'col-md-6'f>>t<'row'<'col-md-6'><'col-md-6'>>",
31
+ "ordering": true,
32
+ "pageLength": 100
41
33
  });
34
+
35
+ var kbHostForm = $('#kb_host-form').detach();
36
+ var leftOfSearchBox = $("#logs-table_wrapper > div:nth-child(1) > div:nth-child(1)");
37
+ kbHostForm.appendTo(leftOfSearchBox);
38
+ kbHostForm.show();
42
39
  });
43
40
  <% end %>
@@ -1,26 +1,21 @@
1
1
  <table id="nodes-table" class="table table-condensed table-striped mobile-data">
2
2
  <thead>
3
3
  <tr>
4
- <th>Node name</th>
5
- <th>Boot time</th>
6
- <th>Updated date</th>
4
+ <th>Node</th>
5
+ <th>Uptime</th>
7
6
  <th>Kill Bill version</th>
8
7
  <th>Dependencies</th>
9
8
  <th>Plugins</th>
10
9
  </tr>
11
10
  </thead>
12
11
  <tbody>
12
+ <%- has_kpm_plugin = kpm_plugin_installed?(nodes_info) %>
13
13
  <% nodes_info.each do |node_info| %>
14
14
  <tr>
15
15
  <td><%= node_info.node_name %></td>
16
16
  <td>
17
17
  <% unless node_info.boot_time.blank? %>
18
- <%= time_ago_in_words(DateTime.parse(node_info.boot_time)) %> ago
19
- <% end %>
20
- </td>
21
- <td>
22
- <% unless node_info.last_updated_date.blank? %>
23
- <%= time_ago_in_words(DateTime.parse(node_info.last_updated_date)) %> ago
18
+ <%= time_ago_in_words(DateTime.parse(node_info.boot_time)) %>
24
19
  <% end %>
25
20
  </td>
26
21
  <td><%= node_info.kb_version %></td>
@@ -39,12 +34,15 @@
39
34
  <li>
40
35
  <%= plugin_info.plugin_name %> <%= plugin_info.version %> <span class="label label-<%= plugin_info.state == 'RUNNING' ? 'success' : (plugin_info.state == 'INSTALLED' ? 'warning' : 'danger') %>"><%= plugin_info.state %></span>
41
36
  <% if plugin_info.state == 'RUNNING' %>
37
+ <%-# If there is not plugin_key, this is most likely a pure OSGI bundle. In that case, we don't want to allow stopping it as we would lose track of it (once it's removed from the OSGI bundle registry, we don't know about it anymore) -%>
38
+ <% unless plugin_info.plugin_key.nil? %>
42
39
  <%= link_to '<i class="fa fa-pause"></i>'.html_safe, plugin_stop_path(:plugin_key => plugin_info.plugin_key, :plugin_name => plugin_info.plugin_name, :plugin_version => plugin_info.version), :method => :post, :title => 'Stop', :remote => true, :class => 'plugin-link' %>
43
- <%= link_to '<i class="fa fa-refresh"></i>'.html_safe, plugin_restart_path(:plugin_key => plugin_info.plugin_key, :plugin_name => plugin_info.plugin_name, :plugin_version => plugin_info.version), :method => :post, :title => 'Restart', :remote => true, :class => 'plugin-link' %>
40
+ <% end %>
41
+ <%= link_to '<i class="fa fa-refresh"></i>'.html_safe, plugin_restart_path(:plugin_key => plugin_info.plugin_key, :plugin_name => plugin_info.plugin_name, :plugin_version => plugin_info.version), :method => :post, :title => 'Restart', :remote => true, :class => 'plugin-link' %>
44
42
  <% elsif plugin_info.state == 'INSTALLED' || plugin_info.state == 'STOPPED' %>
45
43
  <%= link_to '<i class="fa fa-play"></i>'.html_safe, plugin_start_path(:plugin_key => plugin_info.plugin_key, :plugin_name => plugin_info.plugin_name, :plugin_version => plugin_info.version), :method => :post, :title => 'Start', :remote => true, :class => 'plugin-link' %>
46
44
  <% end %>
47
- <% unless plugin_info.version.nil? %>
45
+ <% if !plugin_info.version.nil? && has_kpm_plugin %>
48
46
  <%= link_to '<i class="fa fa-eject"></i>'.html_safe, plugin_uninstall_path(:plugin_key => plugin_info.plugin_key, :plugin_name => plugin_info.plugin_name, :plugin_version => plugin_info.version), :method => :post, :title => 'Uninstall', :remote => true, :class => 'plugin-link' %>
49
47
  <% end %>
50
48
  </li>
@@ -2,7 +2,14 @@
2
2
 
3
3
  <div class="column-block">
4
4
 
5
- <h1>Configured instances</h1>
5
+ <h1>Configured instances
6
+ <%= link_to('<i class="fa fa-refresh"></i>&nbsp;Operation complete, reload page'.html_safe, url_for, :id => "reload-page", :class => 'btn btn-xs text-success', :style => "display: none;") %>
7
+ <% if kpm_plugin_installed?(@nodes_info) %>
8
+ <div class="pull-right">
9
+ <%= link_to 'Install new plugin', plugins_path %>
10
+ </div>
11
+ <% end %>
12
+ </h1>
6
13
 
7
14
  <div id="nodes-table-wrapper">
8
15
  <%= render :partial => 'kpm/nodes_info/nodes_table', :locals => {:nodes_info => @nodes_info} %>
@@ -10,41 +17,22 @@
10
17
 
11
18
  </div>
12
19
 
13
- <div class="column-block">
14
-
15
- <h1><%= link_to 'Install new plugin', plugins_path %></h1>
16
-
17
- </div>
18
-
19
20
  <div class="column-block">
20
21
 
21
22
  <h1>Logs</h1>
22
23
 
23
24
  <div id="logs-table-wrapper">
24
- <%= render :partial => 'kpm/nodes_info/logs_table', :locals => {:logs => @logs} %>
25
+ <%= render :partial => 'kpm/nodes_info/logs_table', :locals => {:kb_host => @kb_host} %>
25
26
  </div>
26
27
 
27
28
  </div>
28
-
29
29
  </div>
30
30
 
31
31
  <%= javascript_tag do %>
32
32
  $(document).ready(function() {
33
- scheduleRefresh();
34
- });
35
-
36
- function scheduleRefresh() {
37
- var timer = setTimeout(refreshAll, 1500);
38
-
39
- // Stop refreshing when user clicks through logs
40
- $('#logs-table_wrapper a').one('click', function() {
41
- clearTimeout(timer);
42
- });
33
+ refreshLogs('<%= nodes_info_refresh_path(:kb_host => @kb_host, :last_event_id => @last_event_id, :format => :js) %>');
43
34
 
44
35
  $('.plugin-link').one('ajax:beforeSend', function() {
45
- // Disable the refresh
46
- clearTimeout(timer);
47
-
48
36
  // Prevent other clicks
49
37
  $('.plugin-link').removeAttr('data-remote')
50
38
  .removeAttr('data-method')
@@ -53,16 +41,15 @@
53
41
  // Spin
54
42
  $(this).children().addClass('fa-spin');
55
43
  });
56
-
57
44
  $('.plugin-link').one('ajax:complete', function() {
58
45
  // Delay a bit, to give time for the plugin state to change before the next refresh
59
- setTimeout(refreshAll, 6000);
46
+ setTimeout(fakeOperationComplete, 6000);
60
47
  });
61
- }
62
48
 
63
- function refreshAll() {
64
- $.ajax({
65
- url: "<%= nodes_info_index_path(:format => :js) %>"
66
- });
67
- }
49
+ function fakeOperationComplete() {
50
+ $('.plugin-link').children().removeClass('fa-spin');
51
+ $('#nodes-table-wrapper').fadeTo("slow", 0.5);
52
+ $('#reload-page').fadeIn("slow");
53
+ }
54
+ });
68
55
  <% end %>
@@ -1,34 +1,34 @@
1
- <%= form_tag plugin_install_from_fs_path, :multipart => true, :class => 'form-horizontal' do %>
1
+ <%= form_tag plugin_install_path, :class => 'form-horizontal' do %>
2
2
  <div class="form-group">
3
- <%= label_tag :key, 'Plugin key', :class => 'col-sm-2 control-label' %>
3
+ <%= label_tag :plugin_key, 'Plugin key', :class => 'col-sm-2 control-label' %>
4
4
  <div class="col-sm-9">
5
- <%= text_field_tag :key, '', :class => 'form-control', :placeholder => 'Plugin key' %>
5
+ <%= text_field_tag :plugin_key, '', :class => 'form-control', :placeholder => 'Plugin key', :required => true %>
6
6
  </div>
7
7
  </div>
8
8
  <div class="form-group">
9
- <%= label_tag :version, 'Version', :class => 'col-sm-2 control-label' %>
9
+ <%= label_tag :plugin_version, 'Version', :class => 'col-sm-2 control-label' %>
10
10
  <div class="col-sm-9">
11
- <%= text_field_tag :version, '', :class => 'form-control', :placeholder => 'Plugin version' %>
11
+ <%= text_field_tag :plugin_version, '', :class => 'form-control', :placeholder => 'Plugin version', :required => true %>
12
+ </div>
13
+ </div>
14
+ <div class="form-group">
15
+ <%= label_tag :plugin_uri, 'URI', :class => 'col-sm-2 control-label' %>
16
+ <div class="col-sm-9">
17
+ <%= text_field_tag :plugin_uri, '', :class => 'form-control', :placeholder => 'Plugin URI', :required => true %>
12
18
  </div>
13
19
  </div>
14
20
  <div class='form-group'>
15
21
  <div class="col-sm-offset-2 col-sm-10">
16
22
  <% %w(java ruby).each do |type| %>
17
23
  <div class="checkbox">
18
- <%= label_tag :type do %>
19
- <%= radio_button_tag :type, type, (type == 'java') %>
24
+ <%= label_tag :plugin_type do %>
25
+ <%= radio_button_tag :plugin_type, type, (type == 'java') %>
20
26
  <%= type %>
21
27
  <% end %>
22
28
  </div>
23
29
  <% end %>
24
30
  </div>
25
31
  </div>
26
- <div class="form-group">
27
- <%= label_tag :plugin, 'Plugin', :class => 'col-sm-2 control-label' %>
28
- <div class="col-sm-10">
29
- <%= file_field_tag 'plugin', :class => 'form-control' %>
30
- </div>
31
- </div>
32
32
  <div class="form-group">
33
33
  <div class="col-sm-offset-2 col-sm-10">
34
34
  <%= submit_tag 'Install', :class => 'btn btn-default' %>
@@ -3,35 +3,15 @@
3
3
  <thead>
4
4
  <tr>
5
5
  <th>Name</th>
6
- <th>Type</th>
7
6
  <th>Version</th>
8
- <th>Required configuration</th>
9
7
  <th></th>
10
8
  </tr>
11
9
  </thead>
12
10
  <tbody>
13
- <% plugins.each do |name, details| %>
11
+ <% plugins.each do |name, version| %>
14
12
  <tr>
15
13
  <td><%= name %></td>
16
- <td><%= details['type'] %></td>
17
- <td>
18
- <% unless details['versions'].nil? %>
19
- <% details['versions'].each do |kb_version, plugin_version| %>
20
- <% if kb_version.eql?(@kb_version) %>
21
- &ensp;<%= plugin_version %>
22
- <% end %>
23
- <% end %>
24
- <% end %>
25
- </td>
26
- <td>
27
- <% unless (details['require'] || []).empty? %>
28
- <ul>
29
- <% details['require'].each do |property| %>
30
- <li><%= property %></li>
31
- <% end %>
32
- </ul>
33
- <% end %>
34
- </td>
14
+ <td><%= version %></td>
35
15
  <td><%= link_to '<i class="fa fa-cloud-download"></i>'.html_safe, plugin_install_path(:plugin_key => name), :method => :post, :title => 'Install' %></td>
36
16
  </tr>
37
17
  <% end %>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div class="column-block">
4
4
 
5
- <h1>Available plugins</h1>
5
+ <h1>Official plugins</h1>
6
6
 
7
7
  <%= render :partial => 'kpm/plugins/plugins_table', :locals => {:plugins => @plugins} %>
8
8
 
data/config/routes.rb CHANGED
@@ -1,11 +1,13 @@
1
- KPM::Engine.routes.draw do
1
+ # frozen_string_literal: true
2
2
 
3
+ KPM::Engine.routes.draw do
3
4
  root to: 'nodes_info#index'
4
5
 
5
6
  resources :nodes_info, :only => [:index]
6
7
  resources :plugins, :only => [:index]
7
8
 
8
9
  scope '/nodes_info' do
10
+ match '/refresh' => 'nodes_info#refresh', :via => :get, :as => 'nodes_info_refresh'
9
11
  match '/plugin/install' => 'nodes_info#install_plugin', :via => :post, :as => 'plugin_install'
10
12
  match '/plugin/install_from_fs' => 'nodes_info#install_plugin_from_fs', :via => :post, :as => 'plugin_install_from_fs'
11
13
  match '/plugin/uninstall' => 'nodes_info#uninstall_plugin', :via => :post, :as => 'plugin_uninstall'
@@ -13,5 +15,4 @@ KPM::Engine.routes.draw do
13
15
  match '/plugin/stop' => 'nodes_info#stop_plugin', :via => :post, :as => 'plugin_stop'
14
16
  match '/plugin/restart' => 'nodes_info#restart_plugin', :via => :post, :as => 'plugin_restart'
15
17
  end
16
-
17
18
  end
data/lib/kpm/client.rb CHANGED
@@ -1,31 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ld-eventsource'
4
+
1
5
  module Killbill
2
6
  module KPM
3
-
4
7
  class KPMClient < KillBillClient::Model::Resource
5
-
6
8
  KILLBILL_KPM_PREFIX = '/plugins/killbill-kpm'
7
9
  KILLBILL_OSGI_LOGGER_PREFIX = '/plugins/killbill-osgi-logger'
8
10
 
9
11
  class << self
12
+ def get_available_plugins(kb_version_maybe_snapshot, latest = true, options = {})
13
+ # Mostly useful for Kill Bill developers: get the latest version before the current snapshot
14
+ # (we rarely deploy SNAPSHOT jars)
15
+ captures = kb_version_maybe_snapshot.scan(/0.(\d+)(\.)?(\d+)?(-SNAPSHOT)?/)
16
+ # [["20", ".", "1", "-SNAPSHOT"]]
17
+ if !captures.nil? && !captures.first.nil? && !captures.first[3].nil?
18
+ if captures.first[2].to_i > 0
19
+ kb_version = '0.' + captures.first[0] + '.' + (captures.first[2].to_i - 1).to_s
20
+ else
21
+ kb_version = '0.' + (captures.first[0].to_i - 1).to_s + '.0'
22
+ end
23
+ else
24
+ kb_version = kb_version_maybe_snapshot
25
+ end
10
26
 
11
- def get_available_plugins(latest=true, options = {})
12
27
  path = "#{KILLBILL_KPM_PREFIX}/plugins"
13
- response = KillBillClient::API.get path, { :latest => latest }, options
14
- JSON.parse(response.body)
15
- end
16
-
17
- def get_osgi_logs(options = {})
18
- response = KillBillClient::API.get KILLBILL_OSGI_LOGGER_PREFIX, {}, options
28
+ response = KillBillClient::API.get path, { :kbVersion => kb_version, :latest => latest }, options
19
29
  JSON.parse(response.body)
20
30
  end
21
31
 
22
- def install_plugin(key, version, type, filename, plugin, options = {})
23
- path = "#{KILLBILL_KPM_PREFIX}/plugins"
24
- KillBillClient::API.post path, plugin, {:key => key, :version => version, :type => type, :filename => filename}, options.merge(:content_type => 'application/octet-stream')
32
+ def stream_osgi_logs(writer, host, last_event_id_ref)
33
+ url = host
34
+ url = "http://#{url}" unless url.starts_with?('http:')
35
+ SSE::Client.new(url + KILLBILL_OSGI_LOGGER_PREFIX, :last_event_id => last_event_id_ref.get, :logger => Rails.logger) do |client|
36
+ client.on_event do |event|
37
+ writer.write(event.data, :id => event.id)
38
+ last_event_id_ref.set(event.id)
39
+ end
40
+ end
25
41
  end
26
42
  end
27
-
28
43
  end
29
-
30
44
  end
31
45
  end
data/lib/kpm/engine.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Dependencies
2
4
  #
3
5
  # Sigh. Rails autoloads the gems specified in the Gemfile and nothing else.
data/lib/kpm/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module KPM
2
- VERSION = '1.0.2'
4
+ VERSION = '2.0.0'
3
5
  end
data/lib/kpm.rb CHANGED
@@ -1,24 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kpm/engine'
2
4
 
3
5
  module KPM
4
-
5
6
  mattr_accessor :current_tenant_user
6
7
  mattr_accessor :layout
7
8
 
8
- self.current_tenant_user = lambda { |session, user|
9
+ self.current_tenant_user = lambda { |_session, _user|
9
10
  {
10
- :username => 'admin',
11
- :password => 'password',
12
- :session_id => nil,
13
- :api_key => KillBillClient.api_key,
14
- :api_secret => KillBillClient.api_secret
11
+ :username => 'admin',
12
+ :password => 'password',
13
+ :session_id => nil,
14
+ :api_key => KillBillClient.api_key,
15
+ :api_secret => KillBillClient.api_secret
15
16
  }
16
17
  }
17
18
 
18
- def self.config(&block)
19
+ def self.config
19
20
  {
20
- :layout => layout || 'kpm/layouts/kpm_application',
21
+ :layout => layout || 'kpm/layouts/kpm_application'
21
22
  }
22
23
  end
23
-
24
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # desc "Explaining what the task does"
2
4
  # task :kpm do
3
5
  # # Task goes here
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class NavigationTest < ActionDispatch::IntegrationTest
4
-
5
6
  include KPM::Engine.routes.url_helpers
6
7
 
7
8
  test 'can see the nodes info page' do
@@ -14,4 +15,3 @@ class NavigationTest < ActionDispatch::IntegrationTest
14
15
  assert_response :success
15
16
  end
16
17
  end
17
-
data/test/kpm_test.rb CHANGED
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
 
3
5
  class KPMTest < ActiveSupport::TestCase
4
-
5
6
  test 'can load KPM module' do
6
7
  assert_kind_of Module, KPM
7
8
  end
data/test/test_helper.rb CHANGED
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Configure Rails Environment
2
- ENV["RAILS_ENV"] = "test"
4
+ ENV['RAILS_ENV'] = 'test'
3
5
 
4
- require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
5
- require "rails/test_help"
6
+ require File.expand_path('../test/dummy/config/environment.rb', __dir__)
7
+ require 'rails/test_help'
6
8
 
7
9
  # Filter out Minitest backtrace while allowing backtrace from other libraries
8
10
  # to be shown.
@@ -13,7 +15,7 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
13
15
 
14
16
  # Load fixtures from the engine
15
17
  if ActiveSupport::TestCase.respond_to?(:fixture_path=)
16
- ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__)
18
+ ActiveSupport::TestCase.fixture_path = File.expand_path('fixtures', __dir__)
17
19
  ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
18
20
  ActiveSupport::TestCase.fixtures :all
19
21
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: killbill-kpm-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kill Bill core team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-16 00:00:00.000000000 Z
11
+ date: 2020-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rails
14
+ name: jquery-datatables-rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.1'
19
+ version: '3.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.1'
26
+ version: '3.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jquery-rails
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,33 +39,33 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '4.3'
41
41
  - !ruby/object:Gem::Dependency
42
- name: jquery-datatables-rails
42
+ name: ld-eventsource
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.3'
47
+ version: 1.0.1
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.3'
54
+ version: 1.0.1
55
55
  - !ruby/object:Gem::Dependency
56
- name: twitter-bootstrap-rails
56
+ name: rails
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '5.1'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '5.1'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: font-awesome-rails
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,22 +86,22 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '2.2'
89
+ version: '3.2'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '2.2'
96
+ version: '3.2'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rake
98
+ name: twitter-bootstrap-rails
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
- type: :development
104
+ type: :runtime
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
@@ -109,7 +109,21 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: simplecov
112
+ name: json
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 1.8.6
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 1.8.6
125
+ - !ruby/object:Gem::Dependency
126
+ name: listen
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
@@ -123,7 +137,7 @@ dependencies:
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
- name: listen
140
+ name: rake
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - ">="
@@ -137,19 +151,33 @@ dependencies:
137
151
  - !ruby/object:Gem::Version
138
152
  version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
- name: json
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.74.0
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.74.0
167
+ - !ruby/object:Gem::Dependency
168
+ name: simplecov
141
169
  requirement: !ruby/object:Gem::Requirement
142
170
  requirements:
143
171
  - - ">="
144
172
  - !ruby/object:Gem::Version
145
- version: 1.8.6
173
+ version: '0'
146
174
  type: :development
147
175
  prerelease: false
148
176
  version_requirements: !ruby/object:Gem::Requirement
149
177
  requirements:
150
178
  - - ">="
151
179
  - !ruby/object:Gem::Version
152
- version: 1.8.6
180
+ version: '0'
153
181
  description: Rails UI plugin for the KPM plugin.
154
182
  email: killbilling-users@googlegroups.com
155
183
  executables: []
@@ -161,9 +189,11 @@ files:
161
189
  - Rakefile
162
190
  - app/assets/javascripts/application.js
163
191
  - app/assets/javascripts/kpm/kpm.js
192
+ - app/assets/javascripts/kpm/logs_refresh.js
164
193
  - app/assets/stylesheets/application.css
165
194
  - app/assets/stylesheets/bootstrap_and_overrides.css
166
195
  - app/assets/stylesheets/kpm/kpm.css
196
+ - app/assets/stylesheets/kpm/nodes_info.css
167
197
  - app/controllers/kpm/engine_controller.rb
168
198
  - app/controllers/kpm/nodes_info_controller.rb
169
199
  - app/controllers/kpm/plugins_controller.rb
@@ -172,7 +202,6 @@ files:
172
202
  - app/views/kpm/nodes_info/_logs_table.html.erb
173
203
  - app/views/kpm/nodes_info/_nodes_table.html.erb
174
204
  - app/views/kpm/nodes_info/index.html.erb
175
- - app/views/kpm/nodes_info/index.js.erb
176
205
  - app/views/kpm/plugins/_form.html.erb
177
206
  - app/views/kpm/plugins/_plugins_table.html.erb
178
207
  - app/views/kpm/plugins/index.html.erb
@@ -236,8 +265,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
236
265
  - !ruby/object:Gem::Version
237
266
  version: '0'
238
267
  requirements: []
239
- rubyforge_project:
240
- rubygems_version: 2.6.13
268
+ rubygems_version: 3.0.6
241
269
  signing_key:
242
270
  specification_version: 4
243
271
  summary: Kill Bill KPM UI mountable engine
@@ -1,4 +0,0 @@
1
- $('#nodes-table-wrapper').html("<%= escape_javascript(render :partial => 'kpm/nodes_info/nodes_table', :locals => {:nodes_info => @nodes_info}) %>");
2
- $('#logs-table-wrapper').html("<%= escape_javascript(render :partial => 'kpm/nodes_info/logs_table', :locals => {:logs => @logs}) %>");
3
-
4
- scheduleRefresh();