diff --git a/src/plugins/status_page/public/status_page.html b/src/plugins/status_page/public/status_page.html
index 09ef885ac32c4..29c83c37eb836 100644
--- a/src/plugins/status_page/public/status_page.html
+++ b/src/plugins/status_page/public/status_page.html
@@ -15,27 +15,48 @@
-
-
Installed Plugins
+
+
Status Breakdown
+
- No plugin status information available
+ No status information available
-
+
+
+ Service |
+ Status |
+
+
+
+ {{status.id}} |
+
+
+ {{status.message}}
+ |
+
+
+
+
- Name |
+ Plugin |
Version |
Status |
-
- {{status.name}} |
- {{status.version}} |
+
+
+ {{status.plugin.id}} |
+ {{status.plugin.version}} |
-
+
{{status.message}}
|
diff --git a/src/plugins/status_page/public/status_page.js b/src/plugins/status_page/public/status_page.js
index 2fc65f299b803..b623340103767 100644
--- a/src/plugins/status_page/public/status_page.js
+++ b/src/plugins/status_page/public/status_page.js
@@ -28,9 +28,11 @@ const chrome = require('ui/chrome')
const data = resp.data;
ui.metrics = data.metrics;
- ui.statuses = data.status.statuses;
ui.name = data.name;
+ ui.statuses = _.groupBy(data.status.statuses, s => s.plugin ? 'plugins' : 'services');
+ if (!_.size(ui.statuses)) ui.statuses = null;
+
const overall = data.status.overall;
if (!ui.serverState || (ui.serverState !== overall.state)) {
ui.serverState = overall.state;
diff --git a/src/plugins/status_page/public/status_page.less b/src/plugins/status_page/public/status_page.less
index c18e5a3ad8c32..c63a323b4c265 100644
--- a/src/plugins/status_page/public/status_page.less
+++ b/src/plugins/status_page/public/status_page.less
@@ -5,9 +5,9 @@
@status-metric-border: #aaa;
@status-metric-title-color: #666;
-@status-plugins-bg: #fff;
-@status-plugins-border: #bbb;
-@status-plugins-headings-color: #666;
+@status-statuses-bg: #fff;
+@status-statuses-border: #bbb;
+@status-statuses-headings-color: #666;
@status-default: #7c7c7c;
@status-green: #94c63d;
@@ -58,13 +58,13 @@
}
}
-// plugin status table section
-.plugin_status_wrapper {
+// status status table section
+.status_status_wrapper {
margin-top: 25px;
margin-left: -5px;
margin-right: -5px;
border-top:2px solid;
- background-color: @status-plugins-bg;
+ background-color: @status-statuses-bg;
padding: 10px;
h3 {
@@ -78,7 +78,7 @@
text-align: center;
}
- .plugin_status_breakdown {
+ .status_status_breakdown {
margin-left: 0;
margin-right: 0;
@@ -86,16 +86,16 @@
height:30px;
line-height:30px;
border-bottom:1px solid;
- border-bottom-color: @status-plugins-border;
+ border-bottom-color: @status-statuses-border;
}
th {
- color:@status-plugins-headings-color;
+ color:@status-statuses-headings-color;
font-weight: normal;
height:25px;
line-height:25px;
border-bottom:1px solid;
- border-bottom-color: @status-plugins-border;
+ border-bottom-color: @status-statuses-border;
}
.status_name {
@@ -111,13 +111,13 @@
}
}
-//plugin state
-.plugin_state(@color, @icon) {
- .plugin_state_color {
+//status state
+.status_state(@color, @icon) {
+ .status_state_color {
color: @color;
}
- .plugin_state_icon:before {
+ .status_state_icon:before {
content: @icon;
}
@@ -130,20 +130,20 @@
}
}
-.plugin_state_default {
- .plugin_state(@status-default, @icon-default);
+.status_state_default {
+ .status_state(@status-default, @icon-default);
}
-.plugin_state_green {
- .plugin_state(@status-green, @icon-green);
+.status_state_green {
+ .status_state(@status-green, @icon-green);
}
-.plugin_state_yellow {
- .plugin_state(@status-yellow, @icon-yellow);
+.status_state_yellow {
+ .status_state(@status-yellow, @icon-yellow);
}
-.plugin_state_red {
- .plugin_state(@status-red, @icon-red);
+.status_state_red {
+ .status_state(@status-red, @icon-red);
}
//server state
@@ -156,7 +156,7 @@
content: @icon;
}
- .plugin_status_wrapper {
+ .status_status_wrapper {
border-top-color: @color;
}
}
diff --git a/src/server/plugins/plugin.js b/src/server/plugins/plugin.js
index 9b805f0c411b4..877ff2d887bb8 100644
--- a/src/server/plugins/plugin.js
+++ b/src/server/plugins/plugin.js
@@ -127,7 +127,7 @@ module.exports = class Plugin {
server.exposeStaticDir(`/plugins/${id}/{path*}`, this.publicDir);
}
- this.status = kbnServer.status.create(this);
+ this.status = kbnServer.status.createForPlugin(this);
server.expose('status', this.status);
return await attempt(this.externalInit, [server, options], this);
diff --git a/src/server/status/__tests__/server_status.js b/src/server/status/__tests__/server_status.js
index 6c3b627c6981a..3d08250fbf81f 100644
--- a/src/server/status/__tests__/server_status.js
+++ b/src/server/status/__tests__/server_status.js
@@ -1,4 +1,4 @@
-import _ from 'lodash';
+import { find } from 'lodash';
import expect from 'expect.js';
import sinon from 'sinon';
@@ -17,31 +17,63 @@ describe('ServerStatus class', function () {
serverStatus = new ServerStatus(server);
});
- describe('#create(plugin)', function () {
+ describe('#create(id)', () => {
+ it('should create a new plugin with an id', () => {
+ const status = serverStatus.create('someid');
+ expect(status).to.be.a(Status);
+ });
+ });
+
+ describe('#createForPlugin(plugin)', function () {
it('should create a new status by plugin', function () {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
expect(status).to.be.a(Status);
});
});
- describe('#get(name)', function () {
- it('exposes plugins by its id/name', function () {
- let status = serverStatus.create(plugin);
- expect(serverStatus.get('name')).to.be(status);
+ describe('#get(id)', () => {
+ it('exposes statuses by their id', () => {
+ const status = serverStatus.create('statusid');
+ expect(serverStatus.get('statusid')).to.be(status);
+ });
+
+ it('does not get the status for a plugin', () => {
+ serverStatus.createForPlugin(plugin);
+ expect(serverStatus.get(plugin)).to.be(undefined);
+ });
+ });
+
+ describe('#getForPluginId(plugin)', function () {
+ it('exposes plugin status for the plugin', function () {
+ let status = serverStatus.createForPlugin(plugin);
+ expect(serverStatus.getForPluginId(plugin.id)).to.be(status);
+ });
+
+ it('does not get plain statuses by their id', function () {
+ serverStatus.create('someid');
+ expect(serverStatus.getForPluginId('someid')).to.be(undefined);
+ });
+ });
+
+ describe('#getState(id)', function () {
+ it('should expose the state of a status by id', function () {
+ let status = serverStatus.create('someid');
+ status.green();
+ expect(serverStatus.getState('someid')).to.be('green');
});
});
- describe('#getState(name)', function () {
- it('should expose the state of the plugin by name', function () {
- let status = serverStatus.create(plugin);
+ describe('#getStateForPluginId(plugin)', function () {
+ it('should expose the state of a plugin by id', function () {
+ let status = serverStatus.createForPlugin(plugin);
status.green();
- expect(serverStatus.getState('name')).to.be('green');
+ expect(serverStatus.getStateForPluginId(plugin.id)).to.be('green');
});
});
describe('#overall()', function () {
it('considers each status to produce a summary', function () {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
expect(serverStatus.overall().state).to.be('uninitialized');
@@ -69,25 +101,24 @@ describe('ServerStatus class', function () {
it('serializes to overall status and individuals', function () {
const pluginOne = {id: 'one', version: '1.0.0'};
const pluginTwo = {id: 'two', version: '2.0.0'};
- const pluginThree = {id: 'three', version: '3.0.0'};
- let one = serverStatus.create(pluginOne);
- let two = serverStatus.create(pluginTwo);
- let three = serverStatus.create(pluginThree);
+ let service = serverStatus.create('some service');
+ let p1 = serverStatus.createForPlugin(pluginOne);
+ let p2 = serverStatus.createForPlugin(pluginTwo);
- one.green();
- two.yellow();
- three.red();
+ service.green();
+ p1.yellow();
+ p2.red();
- let obj = JSON.parse(JSON.stringify(serverStatus));
- expect(obj).to.have.property('overall');
- expect(obj.overall.state).to.eql(serverStatus.overall().state);
- expect(obj.statuses).to.have.length(3);
+ let json = JSON.parse(JSON.stringify(serverStatus));
+ expect(json).to.have.property('overall');
+ expect(json.overall.state).to.eql(serverStatus.overall().state);
+ expect(json.statuses).to.have.length(3);
- let outs = _.indexBy(obj.statuses, 'name');
- expect(outs.one).to.have.property('state', 'green');
- expect(outs.two).to.have.property('state', 'yellow');
- expect(outs.three).to.have.property('state', 'red');
+ const out = status => find(json.statuses, { id: status.id });
+ expect(out(service)).to.have.property('state', 'green');
+ expect(out(p1)).to.have.property('state', 'yellow');
+ expect(out(p2)).to.have.property('state', 'red');
});
});
diff --git a/src/server/status/__tests__/status.js b/src/server/status/__tests__/status.js
index eafeb9f2f7fc6..703c7628da4a6 100644
--- a/src/server/status/__tests__/status.js
+++ b/src/server/status/__tests__/status.js
@@ -15,11 +15,11 @@ describe('Status class', function () {
});
it('should have an "uninitialized" state initially', function () {
- expect(serverStatus.create(plugin)).to.have.property('state', 'uninitialized');
+ expect(serverStatus.createForPlugin(plugin)).to.have.property('state', 'uninitialized');
});
it('emits change when the status is set', function (done) {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
status.once('change', function (prev, prevMsg) {
expect(status.state).to.be('green');
@@ -42,7 +42,7 @@ describe('Status class', function () {
});
it('should only trigger the change listener when something changes', function () {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let stub = sinon.stub();
status.on('change', stub);
status.green('Ready');
@@ -52,18 +52,18 @@ describe('Status class', function () {
});
it('should create a JSON representation of the status', function () {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
status.green('Ready');
let json = status.toJSON();
- expect(json.name).to.eql(plugin.id);
- expect(json.version).to.eql(plugin.version);
+ expect(json.id).to.eql(status.id);
+ expect(json.plugin).to.eql({ id: plugin.id, version: plugin.version });
expect(json.state).to.eql('green');
expect(json.message).to.eql('Ready');
});
it('should call on handler if status is already matched', function (done) {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let msg = 'Test Ready';
status.green(msg);
@@ -77,7 +77,7 @@ describe('Status class', function () {
});
it('should call once handler if status is already matched', function (done) {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let msg = 'Test Ready';
status.green(msg);
@@ -92,7 +92,7 @@ describe('Status class', function () {
function testState(color) {
it(`should change the state to ${color} when #${color}() is called`, function () {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let message = 'testing ' + color;
status[color](message);
expect(status).to.have.property('state', color);
@@ -100,7 +100,7 @@ describe('Status class', function () {
});
it(`should trigger the "change" listner when #${color}() is called`, function (done) {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let message = 'testing ' + color;
status.on('change', function (prev, prevMsg) {
expect(status.state).to.be(color);
@@ -114,7 +114,7 @@ describe('Status class', function () {
});
it(`should trigger the "${color}" listner when #${color}() is called`, function (done) {
- let status = serverStatus.create(plugin);
+ let status = serverStatus.createForPlugin(plugin);
let message = 'testing ' + color;
status.on(color, function (prev, prevMsg) {
expect(status.state).to.be(color);
diff --git a/src/server/status/plugin_status.js b/src/server/status/plugin_status.js
new file mode 100644
index 0000000000000..4f7a60e433f58
--- /dev/null
+++ b/src/server/status/plugin_status.js
@@ -0,0 +1,22 @@
+import _ from 'lodash';
+import states from './states';
+import Status from './status';
+
+class PluginStatus extends Status {
+ constructor(plugin, server) {
+ super(`plugin:${plugin.id}@${plugin.version}`, server);
+ this.plugin = plugin;
+ }
+
+ toJSON() {
+ return {
+ ...super.toJSON(),
+ plugin: {
+ id: this.plugin.id,
+ version: this.plugin.version
+ }
+ };
+ }
+}
+
+module.exports = PluginStatus;
diff --git a/src/server/status/server_status.js b/src/server/status/server_status.js
index 99cc6e69fb7a0..5ec535b668b45 100644
--- a/src/server/status/server_status.js
+++ b/src/server/status/server_status.js
@@ -2,6 +2,7 @@ import _ from 'lodash';
import states from './states';
import Status from './status';
+import PluginStatus from './plugin_status';
module.exports = class ServerStatus {
constructor(server) {
@@ -9,8 +10,16 @@ module.exports = class ServerStatus {
this._created = {};
}
- create(plugin) {
- return (this._created[plugin.id] = new Status(plugin, this.server));
+ create(id) {
+ const status = new Status(id, this.server);
+ this._created[status.id] = status;
+ return status;
+ }
+
+ createForPlugin(plugin) {
+ const status = new PluginStatus(plugin, this.server);
+ this._created[status.id] = status;
+ return status;
}
each(fn) {
@@ -22,12 +31,26 @@ module.exports = class ServerStatus {
});
}
- get(name) {
- return this._created[name];
+ get(id) {
+ return this._created[id];
+ }
+
+ getForPluginId(pluginId) {
+ return _.find(this._created, s =>
+ s.plugin && s.plugin.id === pluginId
+ );
+ }
+
+ getState(id) {
+ const status = this.get(id);
+ if (!status) return undefined;
+ return status.state || 'uninitialized';
}
- getState(name) {
- return _.get(this._created, [name, 'state'], 'uninitialized');
+ getStateForPluginId(pluginId) {
+ const status = this.getForPluginId(pluginId);
+ if (!status) return undefined;
+ return status.state || 'uninitialized';
}
overall() {
diff --git a/src/server/status/status.js b/src/server/status/status.js
index e19278e6e7884..2aefc1fff1d8f 100644
--- a/src/server/status/status.js
+++ b/src/server/status/status.js
@@ -3,18 +3,26 @@ import states from './states';
import { EventEmitter } from 'events';
class Status extends EventEmitter {
- constructor(plugin, server) {
+ constructor(id, server) {
super();
- this.plugin = plugin;
+ if (!id || typeof id !== 'string') {
+ throw new TypeError('Status constructor requires an `id` string');
+ }
+
+ this.id = id;
this.since = new Date();
this.state = 'uninitialized';
this.message = 'uninitialized';
this.on('change', function (previous, previousMsg) {
this.since = new Date();
- let tags = ['status', `plugin:${this.plugin.toString()}`];
- tags.push(this.state === 'red' ? 'error' : 'info');
+
+ const tags = [
+ 'status',
+ this.id,
+ this.state === 'red' ? 'error' : 'info'
+ ];
server.log(tags, {
tmpl: 'Status changed from <%= prevState %> to <%= state %><%= message ? " - " + message : "" %>',
@@ -28,8 +36,7 @@ class Status extends EventEmitter {
toJSON() {
return {
- name: this.plugin.id,
- version: this.plugin.version,
+ id: this.id,
state: this.state,
icon: states.get(this.state).icon,
message: this.message,