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

Install plugin #4406

Merged
merged 14 commits into from
Jul 15, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ target
*.log
esvm
.htpasswd
src/server/bin/plugins
4 changes: 3 additions & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ module.exports = function (grunt) {
'Gruntfile.js',
'<%= root %>/tasks/**/*.js',
'<%= src %>/kibana/*.js',
'<%= src %>/server/**/*.js',
'<%= src %>/server/bin/*.js',
'<%= src %>/server/{config,lib,plugins}/**/*.js',
'<%= src %>/server/bin/{plugin,startup}/**/*.js',
'<%= src %>/kibana/{components,directives,factories,filters,plugins,registry,services,utils}/**/*.js',
'<%= unitTestDir %>/**/*.js',
'!<%= unitTestDir %>/specs/vislib/fixture/**/*'
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"scripts": {
"test": "grunt test",
"start": "node ./src/server/bin/kibana.js",
"postinstall": "bower install && grunt licenses --check-validity",
"precommit": "grunt lintStagedFiles"
},
"repository": {
Expand All @@ -48,6 +47,7 @@
"cookie-parser": "^1.3.3",
"debug": "^2.1.1",
"elasticsearch": "^5.0.0",
"expiry-js": "^0.1.7",
"express": "^4.10.6",
"glob": "^4.3.2",
"good": "^5.1.2",
Expand All @@ -58,12 +58,13 @@
"http-auth": "^2.2.5",
"joi": "^6.4.3",
"js-yaml": "^3.2.5",
"lodash": "^3.9.3",
"json-stringify-safe": "^5.0.1",
"lodash": "^3.9.3",
"moment": "^2.10.3",
"numeral": "^1.5.3",
"request": "^2.40.0",
"requirefrom": "^0.2.0",
"rimraf": "^2.4.0",
"semver": "^4.3.6",
"serve-favicon": "^2.2.0",
"through": "^2.3.6"
Expand Down Expand Up @@ -95,14 +96,14 @@
"husky": "^0.8.1",
"istanbul": "^0.3.15",
"jade": "^1.8.2",
"license-checker": "^3.1.0",
"libesvm": "^1.0.1",
"license-checker": "^3.1.0",
"load-grunt-config": "^0.7.0",
"marked": "^0.3.3",
"marked-text-renderer": "^0.1.0",
"mkdirp": "^0.5.0",
"mocha": "^2.2.5",
"nock": "^1.6.0",
"nock": "^2.7.0",
"npm": "^2.11.0",
"opn": "^1.0.0",
"path-browserify": "0.0.0",
Expand Down
4 changes: 1 addition & 3 deletions src/server/bin/kibana.bat
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,4 @@ TITLE Kibana Server @@version

:finally

ENDLOCAL


ENDLOCAL
60 changes: 10 additions & 50 deletions src/server/bin/kibana.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/usr/bin/env node

var _ = require('lodash');
var Kibana = require('../');
var program = require('commander');
require('../lib/commanderExtensions')(program);
var path = require('path');
var writePidFile = require('../lib/write_pid_file');
var loadSettingsFromYAML = require('../lib/load_settings_from_yaml');
var settings = { 'logging.console.json': true };
var startupOptions = require('./startup/startupOptions');
var startup = require('./startup/startup');
var pluginProgram = require('./plugin/plugin');

var env = (process.env.NODE_ENV) ? process.env.NODE_ENV : 'development';
var packagePath = path.resolve(__dirname, '..', '..', '..', 'package.json');
Expand All @@ -17,51 +16,12 @@ var package = require(packagePath);

program.description('Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch.');
program.version(package.version);
program.option('-e, --elasticsearch <uri>', 'Elasticsearch instance');
program.option('-c, --config <path>', 'Path to the config file');
program.option('-p, --port <port>', 'The port to bind to', parseInt);
program.option('-q, --quiet', 'Turns off logging');
program.option('-H, --host <host>', 'The host to bind to');
program.option('-l, --log-file <path>', 'The file to log to');
program.option('--plugins <path>', 'Path to scan for plugins');
program.parse(process.argv);


if (program.plugins) {
settings['kibana.externalPluginsFolder'] = program.plugins;
}

if (program.elasticsearch) {
settings['elasticsearch.url'] = program.elasticsearch;
}

if (program.port) {
settings['kibana.server.port'] = program.port;
}

if (program.host) {
settings['kibana.server.host'] = program.host;
}

if (program.quiet) {
settings['logging.quiet'] = program.quiet;
}

if (program.logFile) {
settings['logging.file'] = program.logFile;
}

var configPath = program.config || process.env.CONFIG_PATH;
if (configPath) {
settings = _.defaults(settings, loadSettingsFromYAML(configPath));
}

startupOptions(program);
pluginProgram(program);

program.parse(process.argv);

// Start the Kibana server with the settings fromt he CLI and YAML file
var kibana = new Kibana(settings);
kibana.listen()
.then(writePidFile)
.catch(function (err) {
process.exit(1);
});
if (!program.isCommandSpecified()) {
startup(program);
}
36 changes: 36 additions & 0 deletions src/server/bin/plugin/npmInstall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var Promise = require('bluebird');
var fs = require('fs');
var path = require('path');
var exec = require('child_process').exec;

module.exports = function (dest, logger) {
return new Promise(function (resolve, reject) {
//throw an exception if package.json does not exist
try {
var packageFile = path.join(dest, 'package.json');
fs.statSync(packageFile);
} catch (e) {
if (e.code !== 'ENOENT')
throw e;

return reject(new Error('Plugin does not contain package.json file'));
}

var cmd = '"' + path.resolve(path.dirname(process.execPath), 'npm').replace(/\\/g, '/') + '" install --production';

var child = exec(cmd, { cwd: dest });
Copy link
Contributor

Choose a reason for hiding this comment

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

The output of this command needs to be ignored when the --silent option is specified.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified the --silent option so that it applies to both log and error messages per a conversation with @rashidkpc.

child.on('error', function (err) {
reject(err);
});
child.on('exit', function (code, signal) {
if (code === 0) {
resolve();
} else {
reject(new Error('npm install failed with code ' + code));
}
});

logger.error(child.stderr);
logger.log(child.stdout);
});
};
53 changes: 53 additions & 0 deletions src/server/bin/plugin/plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var settingParser = require('./settingParser');
var installer = require('./pluginInstaller');
var remover = require('./pluginRemover');
var pluginLogger = require('./pluginLogger');

module.exports = function (program) {
function processCommand(command, options) {
var settings;
try {
settings = settingParser(command).parse();
} catch (ex) {
//The logger has not yet been initialized.
console.error(ex.message);
Copy link
Contributor

Choose a reason for hiding this comment

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

we need to respect the --silent option here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should already be respecting the silent option. The stdout and stserr streams are passed to the logger. The logger then checks the silent option to determine whether the output should be piped. Any errors ignore the silent option.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified the --silent option so that it applies to both log and error messages per a conversation with @rashidkpc.

Copy link
Contributor

Choose a reason for hiding this comment

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

@BigFunger the logger isn't initialized until the next line.

process.exit(64);
}

var logger = pluginLogger(settings);

if (settings.action === 'install') {
installer.install(settings, logger);
}
if (settings.action === 'remove') {
remover.remove(settings, logger);
}
}

var installDesc =
'The plugin to install\n\n' +
'\tCommon examples:\n' +
'\t -i username/sample\n' +
'\t attempts to download the latest version from the following urls:\n' +
'\t https://download.elastic.co/username/sample/sample-latest.tar.gz\n' +
'\t https://github.com/username/sample/archive/master.tar.gz\n\n' +
'\t -i username/sample/v1.1.1\n' +
'\t attempts to download version v1.1.1 from the following urls:\n' +
'\t https://download.elastic.co/username/sample/sample-v1.1.1.tar.gz\n' +
'\t https://github.com/username/sample/archive/v1.1.1.tar.gz\n\n' +
'\t -i sample -u http://www.example.com/other_name.tar.gz\n' +
'\t attempts to download from the specified url,\n' +
'\t and installs the plugin found at that url as "sample"' +
'\n';

program
.command('plugin')
.description('Maintain Plugins')
.option('-i, --install <org>/<plugin>/<version>', installDesc)
.option('-r, --remove <plugin>', 'The plugin to remove')
.option('-q, --quiet', 'Disable all process messaging except errors')
.option('-s, --silent', 'Disable all process messaging')
.option('-u, --url <url>', 'Specify download url')
.option('-t, --timeout <duration>', 'Length of time before failing; 0 for never fail', settingParser.parseMilliseconds)
.action(processCommand);
};
40 changes: 40 additions & 0 deletions src/server/bin/plugin/pluginCleaner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
var rimraf = require('rimraf');
var fs = require('fs');
var Promise = require('bluebird');

module.exports = function (settings, logger) {

function cleanPrevious() {
return new Promise(function (resolve, reject) {
try {
fs.statSync(settings.workingPath);

logger.log('Found previous install attempt. Deleting...');
try {
rimraf.sync(settings.workingPath);
} catch (e) {
return reject(e);
}
return resolve();
} catch (e) {
if (e.code !== 'ENOENT')
return reject(e);

return resolve();
}
});
}

function cleanError() {
//delete the working directory.
//At this point we're bailing, so swallow any errors on delete.
try {
rimraf.sync(settings.workingPath);
} catch (e) { }
}

return {
cleanPrevious: cleanPrevious,
cleanError: cleanError
};
};
94 changes: 94 additions & 0 deletions src/server/bin/plugin/pluginDownloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
var _ = require('lodash');
var zlib = require('zlib');
var Promise = require('bluebird');
var request = require('request');
var tar = require('tar');
var progressReporter = require('./progressReporter');

module.exports = function (settings, logger) {

//Attempts to download each url in turn until one is successful
function download() {
var urls = settings.urls;

function tryNext() {
var sourceUrl = urls.shift();
if (!sourceUrl) {
throw new Error('Not a valid url.');
}

logger.log('attempting to download ' + sourceUrl);

return Promise.try(function () {
return downloadSingle(sourceUrl, settings.workingPath, settings.timeout, logger)
.catch(function (err) {
if (err.message === 'ENOTFOUND') {
return tryNext();
}
if (err.message === 'EEXTRACT') {
throw (new Error('Error extracting the plugin archive'));
}
throw (err);
});
})
.catch(function (err) {
//Special case for when request.get throws an exception
if (err.message.match(/invalid uri/i)) {
return tryNext();
}
throw (err);
});
}

return tryNext();
}

//Attempts to download a single url
function downloadSingle(source, dest, timeout) {
var gunzip = zlib.createGunzip();
var tarExtract = tar.Extract({ path: dest, strip: 1 });

var requestOptions = { url: source };
if (timeout !== 0) {
requestOptions.timeout = timeout;
}

return wrappedRequest(requestOptions)
.then(function (req) {
//debugger;
var reporter = progressReporter(logger, req);

req
.on('response', reporter.handleResponse)
.on('data', reporter.handleData)
.on('error', _.partial(reporter.handleError, 'ENOTFOUND'))
.pipe(gunzip)
.on('error', _.partial(reporter.handleError, 'EEXTRACT'))
.pipe(tarExtract)
.on('error', _.partial(reporter.handleError, 'EEXTRACT'))
.on('end', reporter.handleEnd);

return reporter.promise;
});
}

function wrappedRequest(requestOptions) {
//debugger;
return Promise.try(function () {
//debugger;
return request.get(requestOptions);
})
.catch(function (err) {
if (err.message.match(/invalid uri/i)) {
throw new Error('ENOTFOUND');
}
throw err;
});
}


return {
download: download,
_downloadSingle: downloadSingle
};
};
Loading