Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check plugin version on startup #8283

Merged
merged 9 commits into from
Sep 19, 2016
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ target
/esvm
.htpasswd
.eslintcache
plugins
/plugins/
data
disabledPlugins
webpackstats.json
Expand Down
2 changes: 1 addition & 1 deletion src/cli_plugin/install/kibana.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'lodash';
import { fromRoot } from '../../utils';
import KbnServer from '../../server/kbn_server';
import readYamlConfig from '../../cli/serve/read_yaml_config';
import { versionSatisfies, cleanVersion } from './version';
import { versionSatisfies, cleanVersion } from '../../utils/version';
import { statSync } from 'fs';

export function existingInstall(settings, logger) {
Expand Down
3 changes: 3 additions & 0 deletions src/server/kbn_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ module.exports = class KbnServer {
// find plugins and set this.plugins
require('./plugins/scan'),

// make sure that all plugins expect the current version of Kibana
require('./plugins/check_version'),

// tell the config we are done loading plugins
require('./config/complete'),

Expand Down
36 changes: 36 additions & 0 deletions src/server/plugins/check_version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { cleanVersion, versionSatisfies } from '../../utils/version';
import { get } from 'lodash';

function compatibleWithKibana(kbnServer, plugin) {
//core plugins have a version of 'kibana' and are always compatible
if (plugin.kibanaVersion === 'kibana') return true;

const pluginKibanaVersion = cleanVersion(plugin.kibanaVersion);
const kibanaVersion = cleanVersion(kbnServer.version);

return versionSatisfies(pluginKibanaVersion, kibanaVersion);
}

export default async function (kbnServer, server, config) {
//because a plugin pack can contain more than one actual plugin, (for example x-pack)
//we make sure that the warning messages are unique
const warningMessages = new Set();
const plugins = kbnServer.plugins;

for (let plugin of plugins) {
const version = plugin.kibanaVersion;
const name = get(plugin, 'pkg.name');

if (!compatibleWithKibana(kbnServer, plugin)) {
const message = `Plugin "${name}" was disabled because it expected Kibana version "${version}", and found "${kbnServer.version}".`;
warningMessages.add(message);
plugins.delete(plugin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation here is really hard for me to read. Would something like this be better?

    if (!versionSatisfies(
        cleanVersion(version),
        cleanVersion(kbnServer.version))) {
      warningMessages.add(`Plugin "${name}" expected Kibana version "${version}" and was disabled.`);
      plugins.delete(plugin);
    }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that @Bargs has pointed it out, I took a deeper look and discovered that Plugin#readConfig() is doing a very different job that it should. Removing the config schema of the plugin from the config service is something that should also happen when a plugin is disabled.

}
}

for (let message of warningMessages) {
server.log(['warning'], message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just log this in the loop above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for multi-plugin packs, I'm checking the version of the pack as a whole, but there will be one plugin object for each plugin in the pack. I only want to show one message for the overall pack.

}

return;
};
23 changes: 13 additions & 10 deletions src/server/plugins/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ module.exports = class Plugin {
this.uiExportsSpecs = opts.uiExports || {};
this.requiredIds = opts.require || [];
this.version = opts.version || pkg.version;

// Plugins must specify their version, and by default that version should match
// the version of kibana down to the patch level. If these two versions need
// to diverge, they can specify a kibana.version in the package to indicate the
// version of kibana the plugin is intended to work with.
this.kibanaVersion = opts.kibanaVersion || _.get(pkg, 'kibana.version', this.version);
this.externalPreInit = opts.preInit || _.noop;
this.externalInit = opts.init || _.noop;
this.configPrefix = opts.configPrefix || this.id;
Expand Down Expand Up @@ -89,17 +95,14 @@ module.exports = class Plugin {
};
}

async readConfig() {
async readConfigSchema() {
let schema = await this.getConfigSchema(Joi);
let { config } = this.kbnServer;
config.extendSchema(this.configPrefix, schema || defaultConfigSchema);

if (config.get([...toPath(this.configPrefix), 'enabled'])) {
return true;
} else {
config.removeSchema(this.configPrefix);
return false;
}
return schema || defaultConfigSchema;
}

get enabled() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's just use a function, getters for this type of stuff can be deceptive.

const { config } = this.kbnServer;
return config.get([...toPath(this.configPrefix), 'enabled']);
}

async preInit() {
Expand Down
37 changes: 27 additions & 10 deletions src/server/plugins/plugin_collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,23 @@
import PluginApi from './plugin_api';
import { inspect } from 'util';
import { get, indexBy } from 'lodash';
import toPath from 'lodash/internal/toPath';
import Collection from '../../utils/collection';

let byIdCache = Symbol('byIdCache');
let pluginApis = Symbol('pluginApis');

async function addPluginConfig(pluginCollection, plugin) {
const configSchema = await plugin.readConfigSchema();
let { config } = pluginCollection.kbnServer;
config.extendSchema(plugin.configPrefix, configSchema);
}

function removePluginConfig(pluginCollection, plugin) {
let { config } = pluginCollection.kbnServer;
config.removeSchema(plugin.configPrefix);
}

module.exports = class Plugins extends Collection {

constructor(kbnServer) {
Expand All @@ -27,21 +39,26 @@ module.exports = class Plugins extends Collection {
// clear the byIdCache
this[byIdCache] = null;

for (let product of output) {

if (product instanceof api.Plugin) {
let plugin = product;
this.add(plugin);

let enabled = await plugin.readConfig();
if (!enabled) this.delete(plugin);
continue;
for (let plugin of output) {
if (!plugin instanceof api.Plugin) {
throw new TypeError('unexpected plugin export ' + inspect(plugin));
}

throw new TypeError('unexpected plugin export ' + inspect(product));
await this.add(plugin);
if (!plugin.enabled) this.delete(plugin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really unclear why these two lines are necessary, I think it would be clearer if we added and removed the configuration from the config schema right here.

}
}

async add(plugin) {
await addPluginConfig(this, plugin);
super.add(plugin);
}

delete(plugin) {
removePluginConfig(this, plugin);
super.delete(plugin);
}

get byId() {
return this[byIdCache] || (this[byIdCache] = indexBy([...this], 'id'));
}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/__tests__/fixtures/plugin_async_foo/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "plugin_async_foo",
"version": "0.0.0"
"version": "kibana"
}
2 changes: 1 addition & 1 deletion src/ui/__tests__/fixtures/plugin_bar/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "plugin_bar",
"version": "0.0.0"
"version": "kibana"
}
2 changes: 1 addition & 1 deletion src/ui/__tests__/fixtures/plugin_foo/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"name": "plugin_foo",
"version": "0.0.0"
"version": "kibana"
}
File renamed without changes.