From 015d3abd83a015e9121fc9b97153b616acf07de0 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Tue, 23 Jun 2015 15:13:18 -0500 Subject: [PATCH 01/12] first pass at plugin installer --- src/server/bin/kibana.js | 59 ++--------- src/server/bin/plugin/downloadAndExpand.js | 49 +++++++++ src/server/bin/plugin/npmInstall.js | 23 +++++ src/server/bin/plugin/plugin.js | 109 +++++++++++++++++++++ src/server/bin/plugin/plugin_install.js | 41 ++++++++ src/server/bin/startup/startup.js | 49 +++++++++ src/server/bin/startup/startup_options.js | 9 ++ src/server/lib/commanderExtensions.js | 9 ++ 8 files changed, 299 insertions(+), 49 deletions(-) create mode 100644 src/server/bin/plugin/downloadAndExpand.js create mode 100644 src/server/bin/plugin/npmInstall.js create mode 100644 src/server/bin/plugin/plugin.js create mode 100644 src/server/bin/plugin/plugin_install.js create mode 100644 src/server/bin/startup/startup.js create mode 100644 src/server/bin/startup/startup_options.js create mode 100644 src/server/lib/commanderExtensions.js diff --git a/src/server/bin/kibana.js b/src/server/bin/kibana.js index 323b5f8546961..c47205a264e97 100755 --- a/src/server/bin/kibana.js +++ b/src/server/bin/kibana.js @@ -1,12 +1,12 @@ #!/usr/bin/env node var _ = require('lodash'); -var Kibana = require('../'); var program = require('commander'); +require('../lib/commanderExtensions.js')(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/startup_options.js'); +var startup = require('./startup/startup.js'); +var pluginProgram = require('./plugin/plugin'); var env = (process.env.NODE_ENV) ? process.env.NODE_ENV : 'development'; var packagePath = path.resolve(__dirname, '..', '..', '..', 'package.json'); @@ -17,51 +17,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 ', 'Elasticsearch instance'); -program.option('-c, --config ', 'Path to the config file'); -program.option('-p, --port ', 'The port to bind to', parseInt); -program.option('-q, --quiet', 'Turns off logging'); -program.option('-H, --host ', 'The host to bind to'); -program.option('-l, --log-file ', 'The file to log to'); -program.option('--plugins ', '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); +} \ No newline at end of file diff --git a/src/server/bin/plugin/downloadAndExpand.js b/src/server/bin/plugin/downloadAndExpand.js new file mode 100644 index 0000000000000..0140963a81e52 --- /dev/null +++ b/src/server/bin/plugin/downloadAndExpand.js @@ -0,0 +1,49 @@ +var _ = require('lodash'); +var zlib = require('zlib'); +var Promise = require('bluebird'); +var request = require('request'); +var tar = require('tar'); +var Path = require('path'); +var fs = Promise.promisifyAll(require('fs')); +var EventEmitter = require('events').EventEmitter; + +module.exports = function (source, dest, downloadLogger) { + downloadLogger = downloadLogger || _.noop; + return new Promise(function (resolve, reject) { + var gunzip = zlib.createGunzip(); + var progress = new EventEmitter(); + + var tarExtract = tar.Extract({ path: dest, strip: 1 }); + + request.get(source) + .on('response', function (resp) { + var total = parseInt(resp.headers['content-length'], 10); + var docInfo = { + level: 'INFO', + type: 'progress', + op: 'downloading', + total: total, + timestamp: new Date(), + message: 'Downloading ' + total + ' bytes' + }; + + downloadLogger(progress, docInfo); + }) + .on('data', function (buffer) { + progress.emit('progress', buffer.length); + }) + .on('error', reject) + .on('end', function () { + progress.emit('message', '\nDownload Complete.\n'); + progress.emit('message', 'Extracting archive.\n'); + }) + .pipe(gunzip) + .on('error', reject) + .pipe(tarExtract) + .on('end', function () { + progress.emit('message', 'Extraction complete.\n'); + resolve(); + }) + .on('error', reject); + }); +}; \ No newline at end of file diff --git a/src/server/bin/plugin/npmInstall.js b/src/server/bin/plugin/npmInstall.js new file mode 100644 index 0000000000000..23b207523f13b --- /dev/null +++ b/src/server/bin/plugin/npmInstall.js @@ -0,0 +1,23 @@ +var npm = require('npm'); +var path = require('path'); +var Promise = require('bluebird'); + +module.exports = function (dest) { + return new Promise(function (resolve, reject) { + //var cwd = process.cwd(); + npm.load(function (err) { + process.chdir(dest); + + var blah = path.join(dest, 'package.json'); + npm.commands.install([blah], function (er, data) { + if (er) { + console.error(er); + } + //process.chdir(cwd); + }); + npm.on('log', function (message) { + console.log(message); + }); + }); + }); +}; \ No newline at end of file diff --git a/src/server/bin/plugin/plugin.js b/src/server/bin/plugin/plugin.js new file mode 100644 index 0000000000000..399269f929e4d --- /dev/null +++ b/src/server/bin/plugin/plugin.js @@ -0,0 +1,109 @@ +module.exports = function (program) { + var expiry = require('expiry-js'); + var downloadAndExpand = require('./downloadAndExpand.js'); + var npmInstall = require('./npmInstall.js'); + var baseUrl = 'https://s3.amazonaws.com/jimtars/'; + var settings; + + function parseSeconds(val) { + var result; + + try { + //Is there is no unit specified, assume seconds + var re = /([a-zA-Z]+)/g; + if (!re.exec(val)) { + val += 's'; + } + + var timeVal = expiry(val); + result = timeVal.asSeconds(); + } catch (ex) { } + + return result; + } + + function parseSettings(options) { + var settings = { + timeout: 0, + silent: false + }; + + if (options.timeout) { + settings.timeout = options.timeout; + } + + if (options.silent) { + settings.silent = options.silent; + } + + if (options.install) { + settings.action = 'install'; + settings.plugin = options.install; + } + + if (options.remove) { + settings.action = 'remove'; + settings.plugin = options.remove; + } + + return settings; + } + + function log(message) { + if (settings.silent) return; + + process.stdout.write(message); + } + + function downloadLogger(progress, docInfo) { + var totalBytes = docInfo.total; + var runningTotal = 0; + + log(docInfo.message + '\n'); + + progress.on('progress', function (data) { + runningTotal += data; + var percent = Math.round(runningTotal / totalBytes * 100); + if (percent % 10 === 0) { + log('.'); + } + }); + + progress.on('message', function (message) { + log(message); + }); + } + + function processCommand(command, options) { + settings = parseSettings(command); + + if (!settings.action) { + console.error('Please specify either --install or --remove.'); + process.exit(1); + } + + if (settings.action === 'install') { + //require('./plugin_install.js')(settings); + log('Running download and install.\n'); + var sourceUrl = 'https://download.elastic.co/kibana/plugins/test-plugin-1.0.0.tgz'; + var destPath = './plugins/' + settings.plugin; + + downloadAndExpand(sourceUrl, destPath, downloadLogger) + .catch(function (e) { + console.error('Error installing plugin: ' + e); + }) + .then(function () { + npmInstall(destPath); + }); + } + } + + program + .command('plugin') + .description('Maintain Plugins') + .option('-i, --install ', 'The plugin to install') + .option('-r, --remove ', 'The plugin to remove') + .option('-s, --silent', 'Disable process messaging') + .option('-t, --timeout ', 'Length of time before failing; 0 for never fail', parseSeconds) + .action(processCommand); +}; \ No newline at end of file diff --git a/src/server/bin/plugin/plugin_install.js b/src/server/bin/plugin/plugin_install.js new file mode 100644 index 0000000000000..f93e400427241 --- /dev/null +++ b/src/server/bin/plugin/plugin_install.js @@ -0,0 +1,41 @@ +module.exports = function (settings) { + var downloadAndExpand = require('./downloadAndExpand.js'); + var npmInstall = require('./npmInstall.js'); + + function log(message) { + if (settings.silent) return; + + process.stdout.write(message); + } + + function downloadLogger(progress, docInfo) { + var totalBytes = docInfo.total; + var runningTotal = 0; + + log(docInfo.message + '\n'); + + progress.on('progress', function (data) { + runningTotal += data; + var percent = Math.round(runningTotal / totalBytes * 100); + if (percent % 10 === 0) { + log('.'); + } + }); + + progress.on('message', function (message) { + log(message); + }); + } + + log('Running download and install.\n'); + var sourceUrl = 'https://download.elastic.co/kibana/plugins/test-plugin-1.0.0.tgz'; + var destPath = './plugins/' + settings.plugin; + + downloadAndExpand(sourceUrl, destPath, downloadLogger) + .catch(function (e) { + console.error('Error installing plugin: ' + e); + }) + .then(function () { + npmInstall(destPath); + }); +}; \ No newline at end of file diff --git a/src/server/bin/startup/startup.js b/src/server/bin/startup/startup.js new file mode 100644 index 0000000000000..6aef334811d41 --- /dev/null +++ b/src/server/bin/startup/startup.js @@ -0,0 +1,49 @@ +module.exports = function (program) { + var _ = require('lodash'); + var path = require('path'); + var Kibana = require('../../'); + var writePidFile = require('../../lib/write_pid_file'); + var loadSettingsFromYAML = require('../../lib/load_settings_from_yaml'); + var settings = { 'logging.console.json': true }; + + function parseSettings() { + 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)); + } + } + + parseSettings(); + + // 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); + }); +}; \ No newline at end of file diff --git a/src/server/bin/startup/startup_options.js b/src/server/bin/startup/startup_options.js new file mode 100644 index 0000000000000..e627a304e3eb5 --- /dev/null +++ b/src/server/bin/startup/startup_options.js @@ -0,0 +1,9 @@ +module.exports = function (program) { + program.option('-e, --elasticsearch ', 'Elasticsearch instance'); + program.option('-c, --config ', 'Path to the config file'); + program.option('-p, --port ', 'The port to bind to', parseInt); + program.option('-q, --quiet', 'Turns off logging'); + program.option('-H, --host ', 'The host to bind to'); + program.option('-l, --log-file ', 'The file to log to'); + program.option('--plugins ', 'Path to scan for plugins'); +}; \ No newline at end of file diff --git a/src/server/lib/commanderExtensions.js b/src/server/lib/commanderExtensions.js new file mode 100644 index 0000000000000..cfc961f7c0738 --- /dev/null +++ b/src/server/lib/commanderExtensions.js @@ -0,0 +1,9 @@ +module.exports = function (program) { + function isCommand(val) { + return typeof val === 'object' && val._name; + } + + program.isCommandSpecified = function () { + return program.args.some(isCommand); + }; +}; \ No newline at end of file From 56d3eba858162782b32a261fcd5b329a3e111d4d Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Tue, 30 Jun 2015 16:09:13 -0500 Subject: [PATCH 02/12] Initial version of plugin installer --- .gitignore | 1 + package.json | 8 +- src/server/bin/kibana.bat | 5 +- src/server/bin/kibana.js | 3 + src/server/bin/plugin/downloadAndExpand.js | 49 -- src/server/bin/plugin/npmInstall.js | 46 +- src/server/bin/plugin/plugin.js | 113 +-- src/server/bin/plugin/pluginCleaner.js | 39 + src/server/bin/plugin/pluginDownloader.js | 65 ++ src/server/bin/plugin/pluginInstaller.js | 40 + src/server/bin/plugin/pluginLogger.js | 41 + src/server/bin/plugin/pluginRemover.js | 25 + src/server/bin/plugin/plugin_install.js | 41 - src/server/bin/plugin/progressReporter.js | 71 ++ src/server/bin/plugin/settingParser.js | 116 +++ test/unit/server/bin/plugin/npmInstall.js | 72 ++ test/unit/server/bin/plugin/plugin.js | 130 +++ test/unit/server/bin/plugin/pluginCleaner.js | 137 ++++ .../server/bin/plugin/pluginDownloader.js | 278 +++++++ .../unit/server/bin/plugin/pluginInstaller.js | 70 ++ test/unit/server/bin/plugin/pluginLogger.js | 102 +++ .../server/bin/plugin/progressReporter.js | 738 ++++++++++++++++++ .../server/bin/plugin/replies/corrupt.tar.gz | Bin 0 -> 1981 bytes .../server/bin/plugin/replies/package.json | 13 + .../bin/plugin/replies/plugin-no-package.gz | Bin 0 -> 533 bytes .../plugin/replies/test-plugin-master.tar.gz | Bin 0 -> 828 bytes test/unit/server/bin/plugin/settingParser.js | 287 +++++++ 27 files changed, 2285 insertions(+), 205 deletions(-) delete mode 100644 src/server/bin/plugin/downloadAndExpand.js create mode 100644 src/server/bin/plugin/pluginCleaner.js create mode 100644 src/server/bin/plugin/pluginDownloader.js create mode 100644 src/server/bin/plugin/pluginInstaller.js create mode 100644 src/server/bin/plugin/pluginLogger.js create mode 100644 src/server/bin/plugin/pluginRemover.js delete mode 100644 src/server/bin/plugin/plugin_install.js create mode 100644 src/server/bin/plugin/progressReporter.js create mode 100644 src/server/bin/plugin/settingParser.js create mode 100644 test/unit/server/bin/plugin/npmInstall.js create mode 100644 test/unit/server/bin/plugin/plugin.js create mode 100644 test/unit/server/bin/plugin/pluginCleaner.js create mode 100644 test/unit/server/bin/plugin/pluginDownloader.js create mode 100644 test/unit/server/bin/plugin/pluginInstaller.js create mode 100644 test/unit/server/bin/plugin/pluginLogger.js create mode 100644 test/unit/server/bin/plugin/progressReporter.js create mode 100644 test/unit/server/bin/plugin/replies/corrupt.tar.gz create mode 100644 test/unit/server/bin/plugin/replies/package.json create mode 100644 test/unit/server/bin/plugin/replies/plugin-no-package.gz create mode 100644 test/unit/server/bin/plugin/replies/test-plugin-master.tar.gz create mode 100644 test/unit/server/bin/plugin/settingParser.js diff --git a/.gitignore b/.gitignore index 4dfc3783e121a..f9eba398ab940 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ target *.log esvm .htpasswd +src/server/bin/plugins \ No newline at end of file diff --git a/package.json b/package.json index 2bf4dc17520e3..a1ed2fb86844e 100644 --- a/package.json +++ b/package.json @@ -58,12 +58,14 @@ "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", + "mockery": "^1.4.0", "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" @@ -95,14 +97,14 @@ "husky": "^0.8.1", "istanbul": "^0.3.15", "jade": "^1.8.2", - "license-checker": "3.0.3", "libesvm": "^1.0.1", + "license-checker": "3.0.3", "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", diff --git a/src/server/bin/kibana.bat b/src/server/bin/kibana.bat index 64849f8d12d99..a7ab6b5c287df 100644 --- a/src/server/bin/kibana.bat +++ b/src/server/bin/kibana.bat @@ -9,6 +9,7 @@ set NODE=%DIR%\node\node.exe set SERVER=%DIR%\src\bin\kibana.js set NODE_ENV="production" set CONFIG_PATH=%DIR%\config\kibana.yml +REM set NPM (TODO: Need to define the env variable to the install of npm. TALK TO CHRIS/JOE) TITLE Kibana Server @@version @@ -16,6 +17,4 @@ TITLE Kibana Server @@version :finally -ENDLOCAL - - +ENDLOCAL \ No newline at end of file diff --git a/src/server/bin/kibana.js b/src/server/bin/kibana.js index c47205a264e97..bbdbcbcfd7979 100755 --- a/src/server/bin/kibana.js +++ b/src/server/bin/kibana.js @@ -1,5 +1,8 @@ #!/usr/bin/env node +//process.env.NODE_DEBUG = 'net'; +//process.env.NODE_DEBUG = 'request'; + var _ = require('lodash'); var program = require('commander'); require('../lib/commanderExtensions.js')(program); diff --git a/src/server/bin/plugin/downloadAndExpand.js b/src/server/bin/plugin/downloadAndExpand.js deleted file mode 100644 index 0140963a81e52..0000000000000 --- a/src/server/bin/plugin/downloadAndExpand.js +++ /dev/null @@ -1,49 +0,0 @@ -var _ = require('lodash'); -var zlib = require('zlib'); -var Promise = require('bluebird'); -var request = require('request'); -var tar = require('tar'); -var Path = require('path'); -var fs = Promise.promisifyAll(require('fs')); -var EventEmitter = require('events').EventEmitter; - -module.exports = function (source, dest, downloadLogger) { - downloadLogger = downloadLogger || _.noop; - return new Promise(function (resolve, reject) { - var gunzip = zlib.createGunzip(); - var progress = new EventEmitter(); - - var tarExtract = tar.Extract({ path: dest, strip: 1 }); - - request.get(source) - .on('response', function (resp) { - var total = parseInt(resp.headers['content-length'], 10); - var docInfo = { - level: 'INFO', - type: 'progress', - op: 'downloading', - total: total, - timestamp: new Date(), - message: 'Downloading ' + total + ' bytes' - }; - - downloadLogger(progress, docInfo); - }) - .on('data', function (buffer) { - progress.emit('progress', buffer.length); - }) - .on('error', reject) - .on('end', function () { - progress.emit('message', '\nDownload Complete.\n'); - progress.emit('message', 'Extracting archive.\n'); - }) - .pipe(gunzip) - .on('error', reject) - .pipe(tarExtract) - .on('end', function () { - progress.emit('message', 'Extraction complete.\n'); - resolve(); - }) - .on('error', reject); - }); -}; \ No newline at end of file diff --git a/src/server/bin/plugin/npmInstall.js b/src/server/bin/plugin/npmInstall.js index 23b207523f13b..28430c9bdb41b 100644 --- a/src/server/bin/plugin/npmInstall.js +++ b/src/server/bin/plugin/npmInstall.js @@ -1,23 +1,37 @@ -var npm = require('npm'); +var Promise = require('bluebird'); +var fs = require('fs'); var path = require('path'); -var Promise = require('bluebird'); +var exec = require('child_process').exec; -module.exports = function (dest) { +module.exports = function (dest, logger) { return new Promise(function (resolve, reject) { - //var cwd = process.cwd(); - npm.load(function (err) { - process.chdir(dest); + //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; - var blah = path.join(dest, 'package.json'); - npm.commands.install([blah], function (er, data) { - if (er) { - console.error(er); - } - //process.chdir(cwd); - }); - npm.on('log', function (message) { - console.log(message); - }); + reject(new Error('Plugin does not contain package.json file')); + } + + var cmd = (process.env.NPM) ? process.env.NPM : 'npm'; + cmd += ' install'; + + var child = exec(cmd, { cwd: dest }); + child.on('error', function (err) { + reject(err); }); + child.on('exit', function (code, signal) { + if (code === 0) { + resolve(); + } else { + reject(new Error('npm install failed.')); + } + }); + + logger.error(child.stderr); + logger.log(child.stdout); }); }; \ No newline at end of file diff --git a/src/server/bin/plugin/plugin.js b/src/server/bin/plugin/plugin.js index 399269f929e4d..b64c1e2189e6d 100644 --- a/src/server/bin/plugin/plugin.js +++ b/src/server/bin/plugin/plugin.js @@ -1,109 +1,36 @@ -module.exports = function (program) { - var expiry = require('expiry-js'); - var downloadAndExpand = require('./downloadAndExpand.js'); - var npmInstall = require('./npmInstall.js'); - var baseUrl = 'https://s3.amazonaws.com/jimtars/'; - var settings; - - function parseSeconds(val) { - var result; +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 { - //Is there is no unit specified, assume seconds - var re = /([a-zA-Z]+)/g; - if (!re.exec(val)) { - val += 's'; - } - - var timeVal = expiry(val); - result = timeVal.asSeconds(); - } catch (ex) { } - - return result; - } - - function parseSettings(options) { - var settings = { - timeout: 0, - silent: false - }; - - if (options.timeout) { - settings.timeout = options.timeout; + settings = settingParser.parse(command); + } catch (ex) { + //The logger has not yet been initialized. + console.error(ex.message); + process.exit(64); } - if (options.silent) { - settings.silent = options.silent; - } - - if (options.install) { - settings.action = 'install'; - settings.plugin = options.install; - } - - if (options.remove) { - settings.action = 'remove'; - settings.plugin = options.remove; - } - - return settings; - } - - function log(message) { - if (settings.silent) return; - - process.stdout.write(message); - } - - function downloadLogger(progress, docInfo) { - var totalBytes = docInfo.total; - var runningTotal = 0; - - log(docInfo.message + '\n'); - - progress.on('progress', function (data) { - runningTotal += data; - var percent = Math.round(runningTotal / totalBytes * 100); - if (percent % 10 === 0) { - log('.'); - } - }); - - progress.on('message', function (message) { - log(message); - }); - } - - function processCommand(command, options) { - settings = parseSettings(command); - - if (!settings.action) { - console.error('Please specify either --install or --remove.'); - process.exit(1); - } + var logger = pluginLogger(settings.silent); if (settings.action === 'install') { - //require('./plugin_install.js')(settings); - log('Running download and install.\n'); - var sourceUrl = 'https://download.elastic.co/kibana/plugins/test-plugin-1.0.0.tgz'; - var destPath = './plugins/' + settings.plugin; - - downloadAndExpand(sourceUrl, destPath, downloadLogger) - .catch(function (e) { - console.error('Error installing plugin: ' + e); - }) - .then(function () { - npmInstall(destPath); - }); + installer.install(settings, logger); + } + if (settings.action === 'remove') { + remover.remove(settings, logger); } } program .command('plugin') .description('Maintain Plugins') - .option('-i, --install ', 'The plugin to install') + .option('-i, --install //', 'The plugin to install') .option('-r, --remove ', 'The plugin to remove') .option('-s, --silent', 'Disable process messaging') - .option('-t, --timeout ', 'Length of time before failing; 0 for never fail', parseSeconds) + .option('-u, --url ', 'Specify download url') + .option('-t, --timeout ', 'Length of time before failing; 0 for never fail', settingParser.parseMilliseconds) .action(processCommand); }; \ No newline at end of file diff --git a/src/server/bin/plugin/pluginCleaner.js b/src/server/bin/plugin/pluginCleaner.js new file mode 100644 index 0000000000000..c069d1f59d9f4 --- /dev/null +++ b/src/server/bin/plugin/pluginCleaner.js @@ -0,0 +1,39 @@ +var rimraf = require('rimraf'); +var fs = require('fs'); +var Promise = require('bluebird'); + +module.exports = { + cleanPrevious: cleanPrevious, + cleanError: cleanError +}; + +function cleanPrevious(settings, logger) { + 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(settings) { + //delete the working directory. + //At this point we're bailing, so swallow any errors on delete. + try { + rimraf.sync(settings.workingPath); + } catch (e) { + console.log(e.message); + } +} \ No newline at end of file diff --git a/src/server/bin/plugin/pluginDownloader.js b/src/server/bin/plugin/pluginDownloader.js new file mode 100644 index 0000000000000..563cea6b87cc2 --- /dev/null +++ b/src/server/bin/plugin/pluginDownloader.js @@ -0,0 +1,65 @@ +var _ = require('lodash'); +var zlib = require('zlib'); +var Promise = require('bluebird'); +var request = require('request'); +var tar = require('tar'); +var path = require('path'); +var progressReporter = require('./progressReporter.js'); + +module.exports = { + download: download, + _downloadSingle: downloadSingle +}; + +//Attempts to download each url in turn until one is successful +function download(settings, logger) { + 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 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); + }); + } + + return tryNext(); +} + +//Attempts to download a single url +function downloadSingle(source, dest, timeout, logger) { + var gunzip = zlib.createGunzip(); + var tarExtract = tar.Extract({ path: dest, strip: 1 }); + + var requestOptions = { url: source }; + if (timeout !== 0) { + requestOptions.timeout = timeout; + } + + var req = request.get(requestOptions); + 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.deferred; +} \ No newline at end of file diff --git a/src/server/bin/plugin/pluginInstaller.js b/src/server/bin/plugin/pluginInstaller.js new file mode 100644 index 0000000000000..0fefb3a7ab845 --- /dev/null +++ b/src/server/bin/plugin/pluginInstaller.js @@ -0,0 +1,40 @@ +var downloader = require('./pluginDownloader.js'); +var cleaner = require('./pluginCleaner.js'); +var npmInstall = require('./npmInstall.js'); +var fs = require('fs'); + +module.exports = { + install: install +}; + +function install(settings, logger) { + logger.log('installing ' + settings.package); + + try { + fs.statSync(settings.pluginPath); + + logger.error('Plugin ' + settings.package + ' already exists. Please remove before installing a new version.'); + process.exit(70); + } catch (e) { + if (e.code !== 'ENOENT') + throw e; + } + + return cleaner.cleanPrevious(settings, logger) + .then(function () { + return downloader.download(settings, logger); + }) + .then(function () { + return npmInstall(settings.workingPath, logger); + }) + .then(function (curious) { + fs.renameSync(settings.workingPath, settings.pluginPath); + logger.log('Plugin installation complete!'); + }) + .catch(function (e) { + logger.error('Plugin installation was unsuccessful.'); + logger.error(e.message); + cleaner.cleanError(settings); + process.exit(70); + }); +} \ No newline at end of file diff --git a/src/server/bin/plugin/pluginLogger.js b/src/server/bin/plugin/pluginLogger.js new file mode 100644 index 0000000000000..73210bda112e1 --- /dev/null +++ b/src/server/bin/plugin/pluginLogger.js @@ -0,0 +1,41 @@ +module.exports = function (silent) { + var previousLineEnded = true; + silent = !!silent; + + function log(data, sameLine) { + if (silent) return; + + if (!sameLine && !previousLineEnded) { + process.stdout.write('\n'); + } + + //if data is a stream, pipe it. + if (data.readable) { + data.pipe(process.stdout); + return; + } + + if (!sameLine) data += '\n'; + process.stdout.write(data); + previousLineEnded = !sameLine; + } + + function error(data) { + if (!previousLineEnded) { + process.stderr.write('\n'); + } + + //if data is a stream, pipe it. + if (data.readable) { + data.pipe(process.stderr); + return; + } + process.stderr.write(data + '\n'); + previousLineEnded = true; + } + + return { + log: log, + error: error + }; +}; \ No newline at end of file diff --git a/src/server/bin/plugin/pluginRemover.js b/src/server/bin/plugin/pluginRemover.js new file mode 100644 index 0000000000000..410384ab2827b --- /dev/null +++ b/src/server/bin/plugin/pluginRemover.js @@ -0,0 +1,25 @@ +var fs = require('fs'); +var rimraf = require('rimraf'); + +module.exports = { + remove: remove +}; + +function remove(settings, logger) { + try { + try { + fs.statSync(settings.pluginPath); + } + catch (e) { + logger.log('Plugin ' + settings.package + ' does not exist.'); + return; + } + + logger.log('Removing ' + settings.package + '...'); + + rimraf.sync(settings.pluginPath); + } catch (ex) { + logger.error(ex.message); + process.exit(74); + } +} \ No newline at end of file diff --git a/src/server/bin/plugin/plugin_install.js b/src/server/bin/plugin/plugin_install.js deleted file mode 100644 index f93e400427241..0000000000000 --- a/src/server/bin/plugin/plugin_install.js +++ /dev/null @@ -1,41 +0,0 @@ -module.exports = function (settings) { - var downloadAndExpand = require('./downloadAndExpand.js'); - var npmInstall = require('./npmInstall.js'); - - function log(message) { - if (settings.silent) return; - - process.stdout.write(message); - } - - function downloadLogger(progress, docInfo) { - var totalBytes = docInfo.total; - var runningTotal = 0; - - log(docInfo.message + '\n'); - - progress.on('progress', function (data) { - runningTotal += data; - var percent = Math.round(runningTotal / totalBytes * 100); - if (percent % 10 === 0) { - log('.'); - } - }); - - progress.on('message', function (message) { - log(message); - }); - } - - log('Running download and install.\n'); - var sourceUrl = 'https://download.elastic.co/kibana/plugins/test-plugin-1.0.0.tgz'; - var destPath = './plugins/' + settings.plugin; - - downloadAndExpand(sourceUrl, destPath, downloadLogger) - .catch(function (e) { - console.error('Error installing plugin: ' + e); - }) - .then(function () { - npmInstall(destPath); - }); -}; \ No newline at end of file diff --git a/src/server/bin/plugin/progressReporter.js b/src/server/bin/plugin/progressReporter.js new file mode 100644 index 0000000000000..13ccee1f9d878 --- /dev/null +++ b/src/server/bin/plugin/progressReporter.js @@ -0,0 +1,71 @@ +var Promise = require('bluebird'); + +/* +Responsible for reporting the progress of the file request stream +*/ +module.exports = function (logger, request) { + var oldDotCount = 0; + var runningTotal = 0; + var totalSize = 0; + var hasError = false; + var _resolve; + var _reject; + var _resp; + + var promise = new Promise(function (resolve, reject) { + _resolve = resolve; + _reject = reject; + }); + + function handleError(errorMessage, err) { + if (hasError) return; + + if (err) logger.error(err); + hasError = true; + request.abort(); + _reject(new Error(errorMessage)); + } + + function handleResponse(resp) { + _resp = resp; + if (resp.statusCode >= 400) { + handleError('ENOTFOUND', null); + } else { + totalSize = parseInt(resp.headers['content-length'], 10) || 0; + var totalDesc = totalSize || 'unknown number of'; + + logger.log('Downloading ' + totalDesc + ' bytes', true); + } + } + + //Should log a dot for every 5% of progress + //Note: no progress is logged if the plugin is downloaded in a single packet + function handleData(buffer) { + if (hasError) return; + if (!totalSize) return; + + runningTotal += buffer.length; + var dotCount = Math.round(runningTotal / totalSize * 100 / 5); + if (dotCount > 20) dotCount = 20; + for (var i = 0; i < (dotCount - oldDotCount) ; i++) { + logger.log('.', true); + } + oldDotCount = dotCount; + } + + function handleEnd() { + if (hasError) return; + + logger.log('Download Complete.'); + _resolve(); + } + + return { + deferred: promise, + handleResponse: handleResponse, + handleError: handleError, + handleData: handleData, + handleEnd: handleEnd, + hasError: function () { return hasError; } + }; +}; diff --git a/src/server/bin/plugin/settingParser.js b/src/server/bin/plugin/settingParser.js new file mode 100644 index 0000000000000..de7ccbdf6ef08 --- /dev/null +++ b/src/server/bin/plugin/settingParser.js @@ -0,0 +1,116 @@ +var path = require('path'); +var expiry = require('expiry-js'); + +module.exports = { + parse: parse, + parseMilliseconds: parseMilliseconds +}; + +function parseMilliseconds(val) { + var result; + + try { + var timeVal = expiry(val); + result = timeVal.asMilliseconds(); + } catch (ex) { + result = 0; + } + + return result; +} + +function generateDownloadUrl(settings) { + var version = (settings.version) || 'latest'; + var filename = settings.package + '-' + version + '.tar.gz'; + + return 'https://download.elastic.co/' + settings.organization + '/' + settings.package + '/' + filename; +} + +function generateGithubUrl(settings) { + var version = (settings.version) || 'master'; + var filename = version + '.tar.gz'; + + return 'https://github.com/' + settings.organization + '/' + settings.package + '/archive/' + filename; +} + +function parse(options) { + var parts; + var settings = { + timeout: 0, + silent: false, + urls: [] + }; + + settings.workingPath = path.resolve(__dirname, '..', 'plugins', '.plugin.installing'); + + if (options.timeout) { + settings.timeout = options.timeout; + } + + if (options.silent) { + settings.silent = options.silent; + } + + if (options.url) { + settings.urls.push(options.url); + } + + if (options.install) { + settings.action = 'install'; + parts = options.install.split('/'); + + if (options.url) { + if (parts.length !== 1) { + throw new Error('Invalid install option. When providing a url, please use the format .'); + } + + settings.package = parts.shift(); + } else { + if (parts.length < 2 || parts.length > 3) { + throw new Error('Invalid install option. Please use the format //.'); + } + + settings.organization = parts.shift(); + settings.package = parts.shift(); + settings.version = parts.shift(); + + + //settings.urls.push('http://www.piyrwljgdambcz.edu/blah.zip'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl1.tar.gz'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl2.tar.gz'); + //settings.urls.push('I should break everything!!!'); + settings.urls.push('https://s3.amazonaws.com/jimtars/badurl3.tar.gz'); + settings.urls.push('https://s3.amazonaws.com/jimtars/badurl4.tar.gz'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl5.tar.gz'); + //settings.urls.push('http://localhost:3000/corrupt.tar.gz'); + //settings.urls.push('https://github.com/spalger/test-plugin/archive/master.tar.gz'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/bowling.tar.gz'); + settings.urls.push('https://s3.amazonaws.com/jimtars/BowlingFull.tar.gz'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl6.tar.gz'); + //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl7.tar.gz'); + + //settings.urls.push(generateDownloadUrl(settings)); + //settings.urls.push(generateGithubUrl(settings)); + } + } + + if (options.remove) { + settings.action = 'remove'; + parts = options.remove.split('/'); + + if (parts.length !== 1) { + throw new Error('Invalid remove option. Please use the format .'); + } + settings.package = parts.shift(); + } + + if (!settings.action || (options.install && options.remove)) { + throw new Error('Please specify either --install or --remove.'); + } + + if (settings.package) { + settings.pluginPath = path.resolve(__dirname, '..', 'plugins', settings.package); + } + + return settings; +} \ No newline at end of file diff --git a/test/unit/server/bin/plugin/npmInstall.js b/test/unit/server/bin/plugin/npmInstall.js new file mode 100644 index 0000000000000..1d37a68e52ebc --- /dev/null +++ b/test/unit/server/bin/plugin/npmInstall.js @@ -0,0 +1,72 @@ +var root = require('requirefrom')(''); +var expect = require('expect.js'); +var sinon = require('sinon'); +var nock = require('nock'); +var glob = require('glob'); +var rimraf = require('rimraf'); +var fs = require('fs'); +var join = require('path').join; +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); +var npmInstall = root('src/server/bin/plugin/npmInstall'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('npmInstall', function () { + + var logger; + var testWorkingPath; + beforeEach(function () { + logger = pluginLogger(false); + testWorkingPath = join(__dirname, '.test.data'); + rimraf.sync(testWorkingPath); + sinon.stub(logger, 'log', function (data, sameLine) { + data.pipe(process.stdout); + }); + sinon.stub(logger, 'error', function (data) { + data.pipe(process.stderr); + }); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); + + it('should throw an error if there is no package.json file in the archive', function () { + fs.mkdir(testWorkingPath, function (e) { }); + + var errorStub = sinon.stub(); + return npmInstall(testWorkingPath, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/package.json/); + }); + }); + + it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { + fs.mkdir(testWorkingPath, function (e) {}); + + sinon.stub(fs, 'statSync', function () { + throw new Error('This is unexpected.'); + }); + + var errorStub = sinon.stub(); + return npmInstall(testWorkingPath, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/This is unexpected./); + + fs.statSync.restore(); + }); + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/plugin.js b/test/unit/server/bin/plugin/plugin.js new file mode 100644 index 0000000000000..86ebe66ae9bea --- /dev/null +++ b/test/unit/server/bin/plugin/plugin.js @@ -0,0 +1,130 @@ +var root = require('requirefrom')(''); +var plugin = root('src/server/bin/plugin/plugin'); +var expect = require('expect.js'); +var sinon = require('sinon'); +var installer = root('src/server/bin/plugin/pluginInstaller'); +var remover = root('src/server/bin/plugin/pluginRemover'); +var settingParser = root('src/server/bin/plugin/settingParser'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('settings.action', function () { + + var program = { + command: function () { return program; }, + description: function () { return program; }, + option: function () { return program; }, + action: function (processCommand) { + processCommand(); + } + }; + + beforeEach(function () { + sinon.stub(remover, 'remove'); + sinon.stub(installer, 'install'); + }); + + afterEach(function () { + remover.remove.restore(); + installer.install.restore(); + settingParser.parse.restore(); + }); + + it('should call remove if settings.action is "remove"', function () { + sinon.stub(settingParser, 'parse', function () { + return { + action: 'remove' + }; + }); + + plugin(program); + + expect(remover.remove.called).to.be(true); + expect(installer.install.called).to.be(false); + }); + + it('should call install if settings.action is "install"', function () { + sinon.stub(settingParser, 'parse', function () { + return { + action: 'install' + }; + }); + + plugin(program); + + expect(remover.remove.called).to.be(false); + expect(installer.install.called).to.be(true); + }); + + }); + + describe('commander options', function () { + + var program = { + command: function () { return program; }, + description: function () { return program; }, + option: function () { return program; }, + action: function () { return program; } + }; + + it('should define the command', function () { + sinon.spy(program, 'command'); + + plugin(program); + expect(program.command.calledWith('plugin')).to.be(true); + + program.command.restore(); + }); + + it('should define the description', function () { + sinon.spy(program, 'description'); + + plugin(program); + expect(program.description.calledWith('Maintain Plugins')).to.be(true); + + program.description.restore(); + }); + + it('should define the command line options', function () { + var spy = sinon.spy(program, 'option'); + + var options = [ + /-i/, + /-r/, + /-s/, + /-u/, + /-t/ + ]; + + plugin(program); + + for (var i = 0; i < spy.callCount; i++) { + var call = spy.getCall(i); + for (var o = 0; o < options.length; o++) { + var option = options[o]; + if (call.args[0].match(option)) { + options.splice(o, 1); + break; + } + } + } + + expect(options).to.have.length(0); + }); + + it('should call the action function', function () { + sinon.spy(program, 'action'); + + plugin(program); + expect(program.action.calledOnce).to.be(true); + + program.action.restore(); + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/pluginCleaner.js b/test/unit/server/bin/plugin/pluginCleaner.js new file mode 100644 index 0000000000000..feac0f3e01aaf --- /dev/null +++ b/test/unit/server/bin/plugin/pluginCleaner.js @@ -0,0 +1,137 @@ +var root = require('requirefrom')(''); +var expect = require('expect.js'); +var sinon = require('sinon'); +var fs = require('fs'); +var rimraf = require('rimraf'); +var cleaner = root('src/server/bin/plugin/pluginCleaner'); +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('pluginCleaner', function () { + + var settings = { + workingPath: 'dummy' + }; + + describe('cleanPrevious', function () { + var errorStub; + var logger; + var progress; + var request; + beforeEach(function () { + errorStub = sinon.stub(); + logger = pluginLogger(false); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + request = { + abort: sinon.stub(), + emit: sinon.stub() + }; + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + fs.statSync.restore(); + rimraf.sync.restore(); + }); + + it('should resolve if the working path does not exist', function () { + sinon.stub(rimraf, 'sync'); + sinon.stub(fs, 'statSync', function () { + var error = new Error('ENOENT'); + error.code = 'ENOENT'; + throw error; + }); + + return cleaner.cleanPrevious(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + }); + }); + + it('should rethrow any exception except ENOENT from fs.statSync', function () { + sinon.stub(rimraf, 'sync'); + sinon.stub(fs, 'statSync', function () { + var error = new Error('An Unhandled Error'); + throw error; + }); + + var errorStub = sinon.stub(); + return cleaner.cleanPrevious(settings, logger) + .catch(errorStub) + .then(function () { + expect(errorStub.called).to.be(true); + }); + }); + + it('should log a message if there was a working directory', function () { + sinon.stub(rimraf, 'sync'); + sinon.stub(fs, 'statSync'); + + return cleaner.cleanPrevious(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(logger.log.calledWith('Found previous install attempt. Deleting...')).to.be(true); + }); + }); + + it('should rethrow any exception from rimraf.sync', function () { + sinon.stub(fs, 'statSync'); + sinon.stub(rimraf, 'sync', function () { + throw new Error('I am an error thrown by rimraf'); + }); + + var errorStub = sinon.stub(); + return cleaner.cleanPrevious(settings, logger) + .catch(errorStub) + .then(function () { + expect(errorStub.called).to.be(true); + }); + }); + + it('should resolve if the working path is deleted', function () { + sinon.stub(rimraf, 'sync'); + sinon.stub(fs, 'statSync'); + + return cleaner.cleanPrevious(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + }); + }); + + }); + + describe('cleanError', function () { + + afterEach(function () { + rimraf.sync.restore(); + }); + + it('should attempt to delete the working directory', function () { + sinon.stub(rimraf, 'sync'); + + cleaner.cleanError(settings); + expect(rimraf.sync.calledWith(settings.workingPath)).to.be(true); + }); + + it('should swallow any errors thrown by rimraf.sync', function () { + sinon.stub(rimraf, 'sync', function () { + throw new Error('Something bad happened.'); + }); + + expect(cleaner.cleanError).withArgs(settings).to.not.throwError(); + }); + + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js new file mode 100644 index 0000000000000..308204b5b137b --- /dev/null +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -0,0 +1,278 @@ +var root = require('requirefrom')(''); +var expect = require('expect.js'); +var sinon = require('sinon'); +var nock = require('nock'); +var glob = require('glob'); +var rimraf = require('rimraf'); +var join = require('path').join; +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); +var downloader = root('src/server/bin/plugin/pluginDownloader'); + +describe('kibana cli', function () { + + describe('plugin downloader', function () { + + var testWorkingPath; + var logger; + + describe('_downloadSingle', function () { + + beforeEach(function () { + logger = pluginLogger(false); + testWorkingPath = join(__dirname, '.test.data'); + rimraf.sync(testWorkingPath); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); + + it('should throw an ENOTFOUND error for a 404 error', function () { + var couchdb = nock('http://www.files.com') + .get('/plugin.tar.gz') + .reply(404); + + var source = 'http://www.files.com/plugin.tar.gz'; + + var errorStub = sinon.stub(); + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + it('download and extract a valid plugin', function () { + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/plugin.tar.gz') + .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz'); + + var source = 'http://www.files.com/plugin.tar.gz'; + + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .then(function (data) { + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]); + }); + }); + + it('should abort the download and extraction for a corrupt archive.', function () { + var couchdb = nock('http://www.files.com') + .get('/plugin.tar.gz') + .replyWithFile(200, __dirname + '/replies/corrupt.tar.gz'); + + var source = 'http://www.files.com/plugin.tar.gz'; + + var errorStub = sinon.stub(); + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + }); + + describe('download', function () { + beforeEach(function () { + logger = pluginLogger(false); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + testWorkingPath = join(__dirname, '.test.data'); + rimraf.sync(testWorkingPath); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); + + it('loop through bad urls until it finds a good one.', function () { + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'http://www.files.com/goodfile.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/goodfile.tar.gz') + .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz'); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + + expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); + expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); + expect(logger.log.getCall(2).args[0]).to.match(/goodfile.tar.gz/); + expect(logger.log.lastCall.args[0]).to.match(/complete/i); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]); + }); + }); + + it('stop looping through urls when it finds a good one.', function () { + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'http://www.files.com/goodfile.tar.gz', + 'http://www.files.com/badfile3.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/goodfile.tar.gz') + .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz') + .get('/badfile3.tar.gz') + .reply(404); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + + for (var i = 0; i < logger.log.callCount; i++) { + expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); + } + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]); + }); + }); + + it('Throw an error when it doesn\'t find a good url.', function () { + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'http://www.files.com/badfile3.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/badfile3.tar.gz') + .reply(404); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/not a valid/i); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + it('Throw an error when it tries to use an invalid url.', function () { + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'I should break everything', + 'http://www.files.com/badfile3.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/badfile3.tar.gz') + .reply(404); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/invalid/i); + + for (var i = 0; i < logger.log.callCount; i++) { + expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); + } + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js new file mode 100644 index 0000000000000..c90f2d8c683ba --- /dev/null +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -0,0 +1,70 @@ +var root = require('requirefrom')(''); +var expect = require('expect.js'); +var sinon = require('sinon'); +var nock = require('nock'); +var glob = require('glob'); +var rimraf = require('rimraf'); +var fs = require('fs'); +var join = require('path').join; +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); +var pluginInstaller = root('src/server/bin/plugin/pluginInstaller'); +var cleaner = root('src/server/bin/plugin/pluginCleaner'); +var Promise = require('bluebird'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('pluginInstaller', function () { + + var logger; + var testWorkingPath; + beforeEach(function () { + logger = pluginLogger(false); + testWorkingPath = join(__dirname, '.test.data'); + rimraf.sync(testWorkingPath); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); + + it('should throw an error if the workingPath already exists.', function () { + sinon.stub(process, 'exit'); + fs.mkdir(testWorkingPath, function (e) {}); + + var settings = { + pluginPath: testWorkingPath + }; + + var errorStub = sinon.stub(); + return pluginInstaller.install(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(logger.error.firstCall.args[0]).to.match(/already exists/); + expect(process.exit.called).to.be(true); + process.exit.restore(); + }); + }); + + it('should rethrow any non "ENOENT" error from fs.', function () { + sinon.stub(fs, 'statSync', function () { + throw new Error('This is unexpected.'); + }); + + var settings = { + pluginPath: testWorkingPath + }; + + expect(pluginInstaller.install).withArgs(settings, logger).to.throwException(/this is unexpected/i); + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/pluginLogger.js b/test/unit/server/bin/plugin/pluginLogger.js new file mode 100644 index 0000000000000..71bf1f77e0111 --- /dev/null +++ b/test/unit/server/bin/plugin/pluginLogger.js @@ -0,0 +1,102 @@ +var root = require('requirefrom')(''); +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); +var expect = require('expect.js'); +var sinon = require('sinon'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('logger', function () { + + var logger; + + describe('logger.log', function () { + + beforeEach(function () { + logger = pluginLogger(false); + sinon.spy(process.stdout, 'write'); + }); + + afterEach(function () { + process.stdout.write.restore(); + }); + + it('should log messages to the console and append a new line', function () { + var message = 'this is my message'; + + logger.log(message); + expect(process.stdout.write.calledWith(message + '\n')).to.be(true); + }); + + it('should log messages to the console and append not append a new line', function () { + for (var i = 0; i < 10; i++) { + logger.log('.', true); + } + logger.log('Done!'); + + expect(process.stdout.write.callCount).to.be(12); + expect(process.stdout.write.getCall(0).args[0]).to.be('.'); + expect(process.stdout.write.getCall(1).args[0]).to.be('.'); + expect(process.stdout.write.getCall(2).args[0]).to.be('.'); + expect(process.stdout.write.getCall(3).args[0]).to.be('.'); + expect(process.stdout.write.getCall(4).args[0]).to.be('.'); + expect(process.stdout.write.getCall(5).args[0]).to.be('.'); + expect(process.stdout.write.getCall(6).args[0]).to.be('.'); + expect(process.stdout.write.getCall(7).args[0]).to.be('.'); + expect(process.stdout.write.getCall(8).args[0]).to.be('.'); + expect(process.stdout.write.getCall(9).args[0]).to.be('.'); + expect(process.stdout.write.getCall(10).args[0]).to.be('\n'); + expect(process.stdout.write.getCall(11).args[0]).to.be('Done!\n'); + }); + + it('should not log any messages when silent is set', function () { + logger = pluginLogger(true); + + var message = 'this is my message'; + logger.log(message); + + for (var i = 0; i < 10; i++) { + logger.log('.', true); + } + logger.log('Done!'); + + expect(process.stdout.write.callCount).to.be(0); + }); + + }); + + describe('logger.error', function () { + + beforeEach(function () { + logger = pluginLogger(false); + sinon.spy(process.stderr, 'write'); + }); + + afterEach(function () { + process.stderr.write.restore(); + }); + + it('should log error messages to the console and append a new line', function () { + var message = 'this is my error'; + + logger.error(message); + expect(process.stderr.write.calledWith(message + '\n')).to.be(true); + }); + + + it('should log error messages to the console regardless of silent setting', function () { + logger = pluginLogger(true); + var message = 'this is my error'; + + logger.error(message); + expect(process.stderr.write.calledWith(message + '\n')).to.be(true); + }); + + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/progressReporter.js b/test/unit/server/bin/plugin/progressReporter.js new file mode 100644 index 0000000000000..58e6a9efb6c2e --- /dev/null +++ b/test/unit/server/bin/plugin/progressReporter.js @@ -0,0 +1,738 @@ +var root = require('requirefrom')(''); +var expect = require('expect.js'); +var sinon = require('sinon'); +var progressReporter = root('src/server/bin/plugin/progressReporter'); +var pluginLogger = root('src/server/bin/plugin/pluginLogger'); + +describe('kibana cli', function () { + + describe('plugin installer', function () { + + describe('progressReporter', function () { + + var logger; + var progress; + var request; + beforeEach(function () { + logger = pluginLogger(false); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + request = { + abort: sinon.stub(), + emit: sinon.stub() + }; + progress = progressReporter(logger, request); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + }); + + describe('handleResponse', function () { + + describe('bad response codes', function () { + + it('should set the state to error for response code = 400', function () { + progress.handleResponse({ statusCode: 400 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 401', function () { + progress.handleResponse({ statusCode: 401 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 402', function () { + progress.handleResponse({ statusCode: 402 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 403', function () { + progress.handleResponse({ statusCode: 403 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 404', function () { + progress.handleResponse({ statusCode: 404 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 405', function () { + progress.handleResponse({ statusCode: 405 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 406', function () { + progress.handleResponse({ statusCode: 406 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 407', function () { + progress.handleResponse({ statusCode: 407 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 408', function () { + progress.handleResponse({ statusCode: 408 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 409', function () { + progress.handleResponse({ statusCode: 409 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 410', function () { + progress.handleResponse({ statusCode: 410 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 411', function () { + progress.handleResponse({ statusCode: 411 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 412', function () { + progress.handleResponse({ statusCode: 412 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 413', function () { + progress.handleResponse({ statusCode: 413 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 414', function () { + progress.handleResponse({ statusCode: 414 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 415', function () { + progress.handleResponse({ statusCode: 415 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 416', function () { + progress.handleResponse({ statusCode: 416 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 417', function () { + progress.handleResponse({ statusCode: 417 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 500', function () { + progress.handleResponse({ statusCode: 500 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 501', function () { + progress.handleResponse({ statusCode: 501 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 502', function () { + progress.handleResponse({ statusCode: 502 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 503', function () { + progress.handleResponse({ statusCode: 503 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 504', function () { + progress.handleResponse({ statusCode: 504 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + it('should set the state to error for response code = 505', function () { + progress.handleResponse({ statusCode: 505 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + + }); + + describe('good response codes', function () { + + it('should set the state to error for response code = 200', function () { + progress.handleResponse({ statusCode: 200, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 201', function () { + progress.handleResponse({ statusCode: 201, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 202', function () { + progress.handleResponse({ statusCode: 202, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 203', function () { + progress.handleResponse({ statusCode: 203, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 204', function () { + progress.handleResponse({ statusCode: 204, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 205', function () { + progress.handleResponse({ statusCode: 205, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 206', function () { + progress.handleResponse({ statusCode: 206, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 300', function () { + progress.handleResponse({ statusCode: 300, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 301', function () { + progress.handleResponse({ statusCode: 301, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 302', function () { + progress.handleResponse({ statusCode: 302, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 303', function () { + progress.handleResponse({ statusCode: 303, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 304', function () { + progress.handleResponse({ statusCode: 304, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 305', function () { + progress.handleResponse({ statusCode: 305, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 306', function () { + progress.handleResponse({ statusCode: 306, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should set the state to error for response code = 307', function () { + progress.handleResponse({ statusCode: 307, headers: { 'content-length': 1000 } }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); + }); + + it('should log "unknown number of" for response codes < 400 without content-length header', function () { + progress.handleResponse({ statusCode: 200, headers: {} }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/unknown number/); + }); + }); + + }); + + }); + + describe('handleData', function () { + + it('should do nothing if the reporter is in an error state', function () { + progress.handleResponse({ statusCode: 400 }); + progress.handleData({ length: 100 }); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(progress.hasError()).to.be(true); + expect(request.abort.called).to.be(true); + expect(logger.log.callCount).to.be(0); + }); + }); + + it('should do nothing if handleResponse hasn\'t successfully executed yet', function () { + progress.handleData({ length: 100 }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(logger.log.callCount).to.be(1); + expect(logger.log.lastCall.args[0]).to.match(/complete/i); + }); + }); + + it('should do nothing if handleResponse was called without a content-length header', function () { + progress.handleResponse({ statusCode: 200, headers: {} }); + progress.handleData({ length: 100 }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(logger.log.callCount).to.be(2); + expect(logger.log.getCall(0).args[0]).to.match(/downloading/i); + expect(logger.log.getCall(1).args[0]).to.match(/complete/i); + }); + }); + + it('should show a max of 20 dots for full prgress', function () { + progress.handleResponse({ statusCode: 200, headers: { 'content-length': 1000 } }); + progress.handleData({ length: 1000 }); + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(logger.log.callCount).to.be(22); + expect(logger.log.getCall(0).args[0]).to.match(/downloading/i); + expect(logger.log.getCall(1).args[0]).to.be('.'); + expect(logger.log.getCall(2).args[0]).to.be('.'); + expect(logger.log.getCall(3).args[0]).to.be('.'); + expect(logger.log.getCall(4).args[0]).to.be('.'); + expect(logger.log.getCall(5).args[0]).to.be('.'); + expect(logger.log.getCall(6).args[0]).to.be('.'); + expect(logger.log.getCall(7).args[0]).to.be('.'); + expect(logger.log.getCall(8).args[0]).to.be('.'); + expect(logger.log.getCall(9).args[0]).to.be('.'); + expect(logger.log.getCall(10).args[0]).to.be('.'); + expect(logger.log.getCall(11).args[0]).to.be('.'); + expect(logger.log.getCall(12).args[0]).to.be('.'); + expect(logger.log.getCall(13).args[0]).to.be('.'); + expect(logger.log.getCall(14).args[0]).to.be('.'); + expect(logger.log.getCall(15).args[0]).to.be('.'); + expect(logger.log.getCall(16).args[0]).to.be('.'); + expect(logger.log.getCall(17).args[0]).to.be('.'); + expect(logger.log.getCall(18).args[0]).to.be('.'); + expect(logger.log.getCall(19).args[0]).to.be('.'); + expect(logger.log.getCall(20).args[0]).to.be('.'); + expect(logger.log.getCall(21).args[0]).to.match(/complete/i); + }); + + }); + + it('should show dot for each 5% of completion', function () { + progress.handleResponse({ statusCode: 200, headers: { 'content-length': 1000 } }); + expect(logger.log.callCount).to.be(1); + + progress.handleData({ length: 50 }); //5% + expect(logger.log.callCount).to.be(2); + + progress.handleData({ length: 100 }); //15% + expect(logger.log.callCount).to.be(4); + + progress.handleData({ length: 200 }); //25% + expect(logger.log.callCount).to.be(8); + + progress.handleData({ length: 590 }); //94% + expect(logger.log.callCount).to.be(20); + + progress.handleData({ length: 60 }); //100% + expect(logger.log.callCount).to.be(21); + + //Any progress over 100% should be ignored. + progress.handleData({ length: 9999 }); + expect(logger.log.callCount).to.be(21); + + progress.handleEnd(); + expect(logger.log.callCount).to.be(22); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(0).args[0]).to.match(/downloading/i); + expect(logger.log.getCall(21).args[0]).to.match(/complete/i); + }); + }); + + }); + + describe('handleEnd', function () { + + it('should reject the deferred with a ENOTFOUND error if the reporter is in an error state', function () { + progress.handleResponse({ statusCode: 400 }); + + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.firstCall.args[0].message).to.match(/ENOTFOUND/); + expect(errorStub.called).to.be(true); + }); + }); + + it('should resolve if the reporter is not in an error state', function () { + progress.handleResponse({ statusCode: 307, headers: { 'content-length': 1000 } }); + + progress.handleEnd(); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.lastCall.args[0]).to.match(/complete/i); + }); + }); + + }); + + describe('handleError', function () { + + it('should log any errors', function () { + progress.handleError('ERRORMESSAGE', new Error('oops!')); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(logger.error.callCount).to.be(1); + expect(logger.error.lastCall.args[0]).to.match(/oops!/); + }); + }); + + it('should set the error state of the reporter', function () { + progress.handleError('ERRORMESSAGE', new Error('oops!')); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(progress.hasError()).to.be(true); + }); + }); + + it('should ignore all errors except the first.', function () { + progress.handleError('ERRORMESSAGE', new Error('oops!')); + progress.handleError('ERRORMESSAGE', new Error('second error!')); + progress.handleError('ERRORMESSAGE', new Error('third error!')); + progress.handleError('ERRORMESSAGE', new Error('fourth error!')); + + var errorStub = sinon.stub(); + return progress.deferred + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(logger.error.callCount).to.be(1); + expect(logger.error.lastCall.args[0]).to.match(/oops!/); + }); + }); + + }); + + }); + + }); + +}); \ No newline at end of file diff --git a/test/unit/server/bin/plugin/replies/corrupt.tar.gz b/test/unit/server/bin/plugin/replies/corrupt.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..d737f1ef2271bea1798031b92c79988371367fd0 GIT binary patch literal 1981 zcmb2|=3oE==C?D>=if3AId1tme8*0PAWjvl>i|Ols%5KX4U;LkQUw6{HWe1+53RpRt z%N;O~Xe;bEDElkU-pj71*1V_PZiiyU;S?D&|GAqN+NnI*H2Y$b6ED}*Yx#$hkF}n; zs469{Gp)t=%06|$v>nZLrh-BW+y@1kC0EUQtN(OXX120Z_dDf~yGkvab84F>s7yM< zv31`|qu{SD;gQ#@;wvZ2Fn($<)kyklmE=)Fmx!%5c~bH(y#IcsDd3&c=vzv`S#)5*Zt3b-WFCm*;2@8 zdUVCB{|}x%fB)vne}#fO#{$(p&b6;UCcMM9GAQ7L|J?3}Ki*vYfBp69{`u?Qnyl15 z=JA&A@bpJ2e_x>YG2ehna-OL0 zQxW&Swn;4Flcu)v%5b06LiS%^OSeu;p9(raG`?F{cZQ1#^iw-Ewo0e8@+IyxxGwy$p g-tz0Mp4FrpNbs;VA4~vJ`P1%S^IkrkL4$z-0Q|_`zW@LL literal 0 HcmV?d00001 diff --git a/test/unit/server/bin/plugin/replies/package.json b/test/unit/server/bin/plugin/replies/package.json new file mode 100644 index 0000000000000..0ee5897991d7d --- /dev/null +++ b/test/unit/server/bin/plugin/replies/package.json @@ -0,0 +1,13 @@ +{ + "name": "test-plugin", + "version": "1.0.0", + "description": "just a test plugin", + "repository": { + "type": "git", + "url": "http://website.git" + }, + "dependencies": { + "bluebird": "2.9.30" + }, + "license": "Apache-2.0" +} \ No newline at end of file diff --git a/test/unit/server/bin/plugin/replies/plugin-no-package.gz b/test/unit/server/bin/plugin/replies/plugin-no-package.gz new file mode 100644 index 0000000000000000000000000000000000000000..8695565d86ff722b7ba77390a4f9a96fc2dca554 GIT binary patch literal 533 zcmV+w0_y!AiwFS5NS###0PUASZ<{a_$9>MH@J>mj6@r8DNYz87^{~UFY1(C{$bpx{ z8`#X4Ez*?lJ_A|XjdVy8LTcmRfhX(-2;TeujSGHQ?=rRF*?JE=1&xMhVTq#1#)dy* zGZ~_Q(2!8JjDmVR2x-934vj!n8m{dlwNmC^c|G%;mwP5qf)kbJAk9t0f|w*B#8J!= z#(~6ix!9y{wv@0Wra%}h<8;d=$njz@P@1bEt9C+O<-9bYeKa(+9vGut{Xfvre?%Cf zXap`?|7Ys&?Sv6KsWhNrX#GEYy8c1O|Dz}%Xq1n5;q{j)g>|mdDuWU^E{-$$|9E%% z_Whlgr)VI0^-sv(`nO@>^gjacFwZf^bsONe4Upr4IHP|dQ#g2EhFcE$^uMH0NB>Kw z|50d{eh&_X(x${Wcw5QD2qp3CtJrYK=lJIYV-2R#5=;1j+hOlR(=#(WX*pfKs&gB7 zQ8Xtfj4&Cj@bpt{Gwqf5wkW2r?=wWAq)pfxQ>=Wy$an%9ZpS~nkLQ{Hj%`*@sRPmb5f(Xaoa+GHXba2xm^`#&3~+y9Ngll8A{@KXM8oS X?RD<_)!}eBo(;bM_{Uh?02}}SjqDPz literal 0 HcmV?d00001 diff --git a/test/unit/server/bin/plugin/replies/test-plugin-master.tar.gz b/test/unit/server/bin/plugin/replies/test-plugin-master.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..0fecb5e016c3b17fc0cc3a9683e230663be9db4e GIT binary patch literal 828 zcmV-C1H=3uiwFP!000001MQg4Z`v>v$2s#?xVZpQ2_zxOpz5L0de~voH0?5}TID1U zaO>Ea03(!5#WuKT1A~eK(AUKDZ%x!(<#bPo1-&J|yI_o6%xISU1JF1ktNKhSozE z`Y-EW{odAp;D!yq_DuHh&h@|N`P&mB$%JtMHN~jv|KR24@9fn7AaLBqQ6}-7KYzv( zxJ`LdP{>c6i{bd}(f`-$tB;?r?KG(?#zFnN?vDN)ZLceV*Ldgp|AcUgFw)BaRmZ^L88h0M_<8W${ClO}2gMKv; z5+Dn~5Q7=2n(Z&;nrUkzLW}MrRNW$kvuu5XkR+1A2(`ZHGE?gVR0ZJmYzBw)A9_3I|Il$AqyHnIHEJ=Of;B?c#$vFvG_3~^xvB-)=Gc9^ zU$rKX$AV;XVYyRN`1}Y_xe3U4P9zsk*0L5@@+m7LsaH&^62Dals#_MypFyPhfPM$H zv~0_fs&M&w7&Wj|vX_{aef3KW>H=!1f{}Ao&M2R>Z!C2;5m*)!>s0e|^u!A^Eg&Ky(b0am_R#kF%lv^V*^pStr8GLWYj4|fz#^WOW`8w} z%6!L3*%;UZ+gmnLp5VN4_H>|S`%FN5zZfG>W2`c!BnFn3(|V<}JB6;!pgI4e?{xe0 zUqoui8b1S-8vncRKkDL}?>|T2<>#-q(V7l{hw8uM?418X*VO-`kieKQXs=Iw0)7@m zKy~cH6ohQIzN@-9#Sef8i)oQSg5(r73;tP4AFn#*d}S~g3\/\/./); + }); + + it('should not allow more than three parts to the install parameter', function () { + options.install = 'kibana/test-plugin/v1.0.1/dummy'; + + expect(settingParser.parse).withArgs(options) + .to.throwException(/Invalid install option. Please use the format \/\/./); + }); + + it('should populate the urls collection properly when no version specified', function () { + options.install = 'kibana/test-plugin'; + + var settings = settingParser.parse(options); + + expect(settings.urls).to.have.property('length', 2); + expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-latest.tar.gz'); + expect(settings.urls).to.contain('https://github.com/kibana/test-plugin/archive/master.tar.gz'); + }); + + it('should populate the urls collection properly version specified', function () { + options.install = 'kibana/test-plugin/v1.1.1'; + + var settings = settingParser.parse(options); + + expect(settings.urls).to.have.property('length', 2); + expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-v1.1.1.tar.gz'); + expect(settings.urls).to.contain('https://github.com/kibana/test-plugin/archive/v1.1.1.tar.gz'); + }); + + it('should populate the pluginPath', function () { + options.install = 'kibana/test-plugin'; + + var settings = settingParser.parse(options); + var expected = path.resolve(__dirname, '..', '..', '..', '..', '..', 'src', 'server', 'bin', 'plugins', 'test-plugin'); + + expect(settings).to.have.property('pluginPath', expected); + }); + + describe('with url option', function () { + + it('should allow one part to the install parameter', function () { + options.install = 'test-plugin'; + options.url = 'http://www.google.com/plugin.tar.gz'; + + expect(settingParser.parse).withArgs(options) + .to.not.throwException(); + + var settings = settingParser.parse(options); + + expect(settings).to.have.property('package', 'test-plugin'); + }); + + it('should not allow more than one part to the install parameter', function () { + options.url = 'http://www.google.com/plugin.tar.gz'; + options.install = 'kibana/test-plugin'; + + expect(settingParser.parse).withArgs(options) + .to.throwException(/Invalid install option. When providing a url, please use the format ./); + }); + + it('should result in only the specified url in urls collection', function () { + var url = 'http://www.google.com/plugin.tar.gz'; + options.install = 'test-plugin'; + options.url = url; + + var settings = settingParser.parse(options); + + expect(settings).to.have.property('urls'); + expect(settings.urls).to.be.an('array'); + expect(settings.urls).to.have.property('length', 1); + expect(settings.urls).to.contain(url); + }); + + }); + + }); + + describe('remove option', function () { + + it('should set settings.action property to "remove"', function () { + options.install = null; + options.remove = 'package'; + + var settings = settingParser.parse(options); + + expect(settings).to.have.property('action', 'remove'); + }); + + it('should allow one part to the remove parameter', function () { + options.install = null; + options.remove = 'test-plugin'; + + var settings = settingParser.parse(options); + + expect(settings).to.have.property('package', 'test-plugin'); + }); + + it('should not allow more than one part to the install parameter', function () { + options.install = null; + options.remove = 'kibana/test-plugin'; + + expect(settingParser.parse).withArgs(options) + .to.throwException(/Invalid remove option. Please use the format ./); + }); + + it('should populate the pluginPath', function () { + options.install = null; + options.remove = 'test-plugin'; + + var settings = settingParser.parse(options); + var expected = path.resolve(__dirname, '..', '..', '..', '..', '..', 'src', 'server', 'bin', 'plugins', 'test-plugin'); + + expect(settings).to.have.property('pluginPath', expected); + }); + + }); + + }); + + }); + + }); + +}); \ No newline at end of file From a9b729655d1dcf710a3b00d5e572b3d2c1bee8db Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 09:29:05 -0500 Subject: [PATCH 03/12] small fixes for issues found during code review/pull submission --- package.json | 1 - src/server/bin/kibana.js | 4 ---- src/server/bin/plugin/settingParser.js | 19 ++----------------- .../{startup_options.js => startupOptions.js} | 0 4 files changed, 2 insertions(+), 22 deletions(-) rename src/server/bin/startup/{startup_options.js => startupOptions.js} (100%) diff --git a/package.json b/package.json index 399066f02dd37..6ec028fae773f 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,6 @@ "js-yaml": "^3.2.5", "json-stringify-safe": "^5.0.1", "lodash": "^3.9.3", - "mockery": "^1.4.0", "moment": "^2.10.3", "numeral": "^1.5.3", "request": "^2.40.0", diff --git a/src/server/bin/kibana.js b/src/server/bin/kibana.js index bbdbcbcfd7979..f3fca70cd4893 100755 --- a/src/server/bin/kibana.js +++ b/src/server/bin/kibana.js @@ -1,9 +1,5 @@ #!/usr/bin/env node -//process.env.NODE_DEBUG = 'net'; -//process.env.NODE_DEBUG = 'request'; - -var _ = require('lodash'); var program = require('commander'); require('../lib/commanderExtensions.js')(program); var path = require('path'); diff --git a/src/server/bin/plugin/settingParser.js b/src/server/bin/plugin/settingParser.js index de7ccbdf6ef08..a6006a99c3e3c 100644 --- a/src/server/bin/plugin/settingParser.js +++ b/src/server/bin/plugin/settingParser.js @@ -74,23 +74,8 @@ function parse(options) { settings.package = parts.shift(); settings.version = parts.shift(); - - //settings.urls.push('http://www.piyrwljgdambcz.edu/blah.zip'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl1.tar.gz'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl2.tar.gz'); - //settings.urls.push('I should break everything!!!'); - settings.urls.push('https://s3.amazonaws.com/jimtars/badurl3.tar.gz'); - settings.urls.push('https://s3.amazonaws.com/jimtars/badurl4.tar.gz'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl5.tar.gz'); - //settings.urls.push('http://localhost:3000/corrupt.tar.gz'); - //settings.urls.push('https://github.com/spalger/test-plugin/archive/master.tar.gz'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/bowling.tar.gz'); - settings.urls.push('https://s3.amazonaws.com/jimtars/BowlingFull.tar.gz'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl6.tar.gz'); - //settings.urls.push('https://s3.amazonaws.com/jimtars/badurl7.tar.gz'); - - //settings.urls.push(generateDownloadUrl(settings)); - //settings.urls.push(generateGithubUrl(settings)); + settings.urls.push(generateDownloadUrl(settings)); + settings.urls.push(generateGithubUrl(settings)); } } diff --git a/src/server/bin/startup/startup_options.js b/src/server/bin/startup/startupOptions.js similarity index 100% rename from src/server/bin/startup/startup_options.js rename to src/server/bin/startup/startupOptions.js From ca224a4b3b1457400a8b128574e93ec5e63b25b9 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 10:01:04 -0500 Subject: [PATCH 04/12] fixed reference to renamed file, set default ENV variable for npm --- package.json | 1 + src/server/bin/kibana.bat | 2 +- src/server/bin/kibana.js | 2 +- .../server/bin/plugin/pluginDownloader.js | 23 +++++++++++-------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 6ec028fae773f..2919bfd7f6655 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,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", diff --git a/src/server/bin/kibana.bat b/src/server/bin/kibana.bat index a7ab6b5c287df..cb28a95fb7311 100644 --- a/src/server/bin/kibana.bat +++ b/src/server/bin/kibana.bat @@ -9,7 +9,7 @@ set NODE=%DIR%\node\node.exe set SERVER=%DIR%\src\bin\kibana.js set NODE_ENV="production" set CONFIG_PATH=%DIR%\config\kibana.yml -REM set NPM (TODO: Need to define the env variable to the install of npm. TALK TO CHRIS/JOE) +set NPM=npm TITLE Kibana Server @@version diff --git a/src/server/bin/kibana.js b/src/server/bin/kibana.js index f3fca70cd4893..5c3b44a860b2f 100755 --- a/src/server/bin/kibana.js +++ b/src/server/bin/kibana.js @@ -3,7 +3,7 @@ var program = require('commander'); require('../lib/commanderExtensions.js')(program); var path = require('path'); -var startupOptions = require('./startup/startup_options.js'); +var startupOptions = require('./startup/startupOptions.js'); var startup = require('./startup/startup.js'); var pluginProgram = require('./plugin/plugin'); diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index 308204b5b137b..8951c066f195a 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -50,13 +50,14 @@ describe('kibana cli', function () { }); }); - it('download and extract a valid plugin', function () { + it('should download and extract a valid plugin', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); var couchdb = nock('http://www.files.com') .defaultReplyHeaders({ 'content-length': '10' }) .get('/plugin.tar.gz') - .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz'); + .replyWithFile(200, filename); var source = 'http://www.files.com/plugin.tar.gz'; @@ -74,9 +75,10 @@ describe('kibana cli', function () { }); it('should abort the download and extraction for a corrupt archive.', function () { + var filename = join(__dirname, 'replies/corrupt.tar.gz'); var couchdb = nock('http://www.files.com') .get('/plugin.tar.gz') - .replyWithFile(200, __dirname + '/replies/corrupt.tar.gz'); + .replyWithFile(200, filename); var source = 'http://www.files.com/plugin.tar.gz'; @@ -94,6 +96,7 @@ describe('kibana cli', function () { }); describe('download', function () { + beforeEach(function () { logger = pluginLogger(false); sinon.stub(logger, 'log'); @@ -108,7 +111,8 @@ describe('kibana cli', function () { rimraf.sync(testWorkingPath); }); - it('loop through bad urls until it finds a good one.', function () { + it('should loop through bad urls until it finds a good one.', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); var settings = { urls: [ 'http://www.files.com/badfile1.tar.gz', @@ -128,7 +132,7 @@ describe('kibana cli', function () { .get('/badfile2.tar.gz') .reply(404) .get('/goodfile.tar.gz') - .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz'); + .replyWithFile(200, filename); var errorStub = sinon.stub(); return downloader.download(settings, logger) @@ -152,7 +156,8 @@ describe('kibana cli', function () { }); }); - it('stop looping through urls when it finds a good one.', function () { + it('should stop looping through urls when it finds a good one.', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); var settings = { urls: [ 'http://www.files.com/badfile1.tar.gz', @@ -173,7 +178,7 @@ describe('kibana cli', function () { .get('/badfile2.tar.gz') .reply(404) .get('/goodfile.tar.gz') - .replyWithFile(200, __dirname + '/replies/test-plugin-master.tar.gz') + .replyWithFile(200, filename) .get('/badfile3.tar.gz') .reply(404); @@ -198,7 +203,7 @@ describe('kibana cli', function () { }); }); - it('Throw an error when it doesn\'t find a good url.', function () { + it('should throw an error when it doesn\'t find a good url.', function () { var settings = { urls: [ 'http://www.files.com/badfile1.tar.gz', @@ -232,7 +237,7 @@ describe('kibana cli', function () { }); }); - it('Throw an error when it tries to use an invalid url.', function () { + it('should throw an error when it tries to use an invalid url.', function () { var settings = { urls: [ 'http://www.files.com/badfile1.tar.gz', From 4a021a2496efe18dda972ccfa3e50181beb0a6a1 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 11:44:50 -0500 Subject: [PATCH 05/12] Refactored a few files to tighten scope --- src/server/bin/plugin/pluginCleaner.js | 59 ++++++------ src/server/bin/plugin/pluginDownloader.js | 96 ++++++++++--------- src/server/bin/plugin/pluginInstaller.js | 13 ++- test/unit/server/bin/plugin/pluginCleaner.js | 22 +++-- .../server/bin/plugin/pluginDownloader.js | 9 +- .../unit/server/bin/plugin/pluginInstaller.js | 1 - 6 files changed, 110 insertions(+), 90 deletions(-) diff --git a/src/server/bin/plugin/pluginCleaner.js b/src/server/bin/plugin/pluginCleaner.js index c069d1f59d9f4..0165caa116fc7 100644 --- a/src/server/bin/plugin/pluginCleaner.js +++ b/src/server/bin/plugin/pluginCleaner.js @@ -2,38 +2,39 @@ var fs = require('fs'); var Promise = require('bluebird'); -module.exports = { - cleanPrevious: cleanPrevious, - cleanError: cleanError -}; +module.exports = function (settings, logger) { -function cleanPrevious(settings, logger) { - return new Promise(function (resolve, reject) { - try { - fs.statSync(settings.workingPath); - - logger.log('Found previous install attempt. Deleting...'); + function cleanPrevious() { + return new Promise(function (resolve, reject) { try { - rimraf.sync(settings.workingPath); + 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) { - return reject(e); - } - return resolve(); - } catch (e) { - if (e.code !== 'ENOENT') - return reject(e); + if (e.code !== 'ENOENT') + return reject(e); - return resolve(); - } - }); -} + return resolve(); + } + }); + } -function cleanError(settings) { - //delete the working directory. - //At this point we're bailing, so swallow any errors on delete. - try { - rimraf.sync(settings.workingPath); - } catch (e) { - console.log(e.message); + 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) { } } -} \ No newline at end of file + + return { + cleanPrevious: cleanPrevious, + cleanError: cleanError + }; +}; \ No newline at end of file diff --git a/src/server/bin/plugin/pluginDownloader.js b/src/server/bin/plugin/pluginDownloader.js index 563cea6b87cc2..a64d3865e5511 100644 --- a/src/server/bin/plugin/pluginDownloader.js +++ b/src/server/bin/plugin/pluginDownloader.js @@ -3,63 +3,65 @@ var zlib = require('zlib'); var Promise = require('bluebird'); var request = require('request'); var tar = require('tar'); -var path = require('path'); var progressReporter = require('./progressReporter.js'); -module.exports = { - download: download, - _downloadSingle: downloadSingle -}; +module.exports = function (settings, logger) { -//Attempts to download each url in turn until one is successful -function download(settings, logger) { - var urls = settings.urls; + //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.'); - } + function tryNext() { + var sourceUrl = urls.shift(); + if (!sourceUrl) { + throw new Error('Not a valid url.'); + } - logger.log('attempting to download ' + sourceUrl); + logger.log('attempting to download ' + sourceUrl); - 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); - }); + 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); + }); + } + + return tryNext(); } - 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 }); -//Attempts to download a single url -function downloadSingle(source, dest, timeout, logger) { - var gunzip = zlib.createGunzip(); - var tarExtract = tar.Extract({ path: dest, strip: 1 }); + var requestOptions = { url: source }; + if (timeout !== 0) { + requestOptions.timeout = timeout; + } - var requestOptions = { url: source }; - if (timeout !== 0) { - requestOptions.timeout = timeout; - } + var req = request.get(requestOptions); + var reporter = progressReporter(logger, req); - var req = request.get(requestOptions); - 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); - 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.deferred; + } - return reporter.deferred; -} \ No newline at end of file + return { + download: download, + _downloadSingle: downloadSingle + }; +}; \ No newline at end of file diff --git a/src/server/bin/plugin/pluginInstaller.js b/src/server/bin/plugin/pluginInstaller.js index 0fefb3a7ab845..5901230290e69 100644 --- a/src/server/bin/plugin/pluginInstaller.js +++ b/src/server/bin/plugin/pluginInstaller.js @@ -1,5 +1,5 @@ -var downloader = require('./pluginDownloader.js'); -var cleaner = require('./pluginCleaner.js'); +var pluginDownloader = require('./pluginDownloader.js'); +var pluginCleaner = require('./pluginCleaner.js'); var npmInstall = require('./npmInstall.js'); var fs = require('fs'); @@ -20,9 +20,12 @@ function install(settings, logger) { throw e; } - return cleaner.cleanPrevious(settings, logger) + var cleaner = pluginCleaner(settings, logger); + var downloader = pluginDownloader(settings, logger); + + return cleaner.cleanPrevious() .then(function () { - return downloader.download(settings, logger); + return downloader.download(); }) .then(function () { return npmInstall(settings.workingPath, logger); @@ -34,7 +37,7 @@ function install(settings, logger) { .catch(function (e) { logger.error('Plugin installation was unsuccessful.'); logger.error(e.message); - cleaner.cleanError(settings); + cleaner.cleanError(); process.exit(70); }); } \ No newline at end of file diff --git a/test/unit/server/bin/plugin/pluginCleaner.js b/test/unit/server/bin/plugin/pluginCleaner.js index feac0f3e01aaf..41437d8e8b3f4 100644 --- a/test/unit/server/bin/plugin/pluginCleaner.js +++ b/test/unit/server/bin/plugin/pluginCleaner.js @@ -3,7 +3,7 @@ var expect = require('expect.js'); var sinon = require('sinon'); var fs = require('fs'); var rimraf = require('rimraf'); -var cleaner = root('src/server/bin/plugin/pluginCleaner'); +var pluginCleaner = root('src/server/bin/plugin/pluginCleaner'); var pluginLogger = root('src/server/bin/plugin/pluginLogger'); describe('kibana cli', function () { @@ -17,6 +17,7 @@ describe('kibana cli', function () { }; describe('cleanPrevious', function () { + var cleaner; var errorStub; var logger; var progress; @@ -24,6 +25,7 @@ describe('kibana cli', function () { beforeEach(function () { errorStub = sinon.stub(); logger = pluginLogger(false); + cleaner = pluginCleaner(settings, logger); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); request = { @@ -47,7 +49,7 @@ describe('kibana cli', function () { throw error; }); - return cleaner.cleanPrevious(settings, logger) + return cleaner.cleanPrevious(logger) .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -62,7 +64,7 @@ describe('kibana cli', function () { }); var errorStub = sinon.stub(); - return cleaner.cleanPrevious(settings, logger) + return cleaner.cleanPrevious(logger) .catch(errorStub) .then(function () { expect(errorStub.called).to.be(true); @@ -73,7 +75,7 @@ describe('kibana cli', function () { sinon.stub(rimraf, 'sync'); sinon.stub(fs, 'statSync'); - return cleaner.cleanPrevious(settings, logger) + return cleaner.cleanPrevious(logger) .catch(errorStub) .then(function (data) { expect(logger.log.calledWith('Found previous install attempt. Deleting...')).to.be(true); @@ -87,7 +89,7 @@ describe('kibana cli', function () { }); var errorStub = sinon.stub(); - return cleaner.cleanPrevious(settings, logger) + return cleaner.cleanPrevious(logger) .catch(errorStub) .then(function () { expect(errorStub.called).to.be(true); @@ -98,7 +100,7 @@ describe('kibana cli', function () { sinon.stub(rimraf, 'sync'); sinon.stub(fs, 'statSync'); - return cleaner.cleanPrevious(settings, logger) + return cleaner.cleanPrevious(logger) .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -108,6 +110,12 @@ describe('kibana cli', function () { }); describe('cleanError', function () { + var cleaner; + var logger; + beforeEach(function () { + logger = pluginLogger(false); + cleaner = pluginCleaner(settings, logger); + }); afterEach(function () { rimraf.sync.restore(); @@ -116,7 +124,7 @@ describe('kibana cli', function () { it('should attempt to delete the working directory', function () { sinon.stub(rimraf, 'sync'); - cleaner.cleanError(settings); + cleaner.cleanError(); expect(rimraf.sync.calledWith(settings.workingPath)).to.be(true); }); diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index 8951c066f195a..5e2a835f79110 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -6,7 +6,7 @@ var glob = require('glob'); var rimraf = require('rimraf'); var join = require('path').join; var pluginLogger = root('src/server/bin/plugin/pluginLogger'); -var downloader = root('src/server/bin/plugin/pluginDownloader'); +var pluginDownloader = root('src/server/bin/plugin/pluginDownloader'); describe('kibana cli', function () { @@ -14,11 +14,13 @@ describe('kibana cli', function () { var testWorkingPath; var logger; + var downloader; describe('_downloadSingle', function () { beforeEach(function () { logger = pluginLogger(false); + downloader = pluginDownloader({}, logger); testWorkingPath = join(__dirname, '.test.data'); rimraf.sync(testWorkingPath); sinon.stub(logger, 'log'); @@ -97,6 +99,7 @@ describe('kibana cli', function () { describe('download', function () { + var downloader; beforeEach(function () { logger = pluginLogger(false); sinon.stub(logger, 'log'); @@ -122,6 +125,7 @@ describe('kibana cli', function () { workingPath: testWorkingPath, timeout: 0 }; + downloader = pluginDownloader(settings, logger); var couchdb = nock('http://www.files.com') .defaultReplyHeaders({ @@ -168,6 +172,7 @@ describe('kibana cli', function () { workingPath: testWorkingPath, timeout: 0 }; + downloader = pluginDownloader(settings, logger); var couchdb = nock('http://www.files.com') .defaultReplyHeaders({ @@ -213,6 +218,7 @@ describe('kibana cli', function () { workingPath: testWorkingPath, timeout: 0 }; + downloader = pluginDownloader(settings, logger); var couchdb = nock('http://www.files.com') .defaultReplyHeaders({ @@ -248,6 +254,7 @@ describe('kibana cli', function () { workingPath: testWorkingPath, timeout: 0 }; + downloader = pluginDownloader(settings, logger); var couchdb = nock('http://www.files.com') .defaultReplyHeaders({ diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js index c90f2d8c683ba..49eaa1d985384 100644 --- a/test/unit/server/bin/plugin/pluginInstaller.js +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -8,7 +8,6 @@ var fs = require('fs'); var join = require('path').join; var pluginLogger = root('src/server/bin/plugin/pluginLogger'); var pluginInstaller = root('src/server/bin/plugin/pluginInstaller'); -var cleaner = root('src/server/bin/plugin/pluginCleaner'); var Promise = require('bluebird'); describe('kibana cli', function () { From c0a6572c1540a43da013f1d1bef561be9fa4a1cc Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 14:57:05 -0500 Subject: [PATCH 06/12] Changes mkdir to sync in tests. Wrapped exception from request.get --- src/server/bin/plugin/pluginDownloader.js | 61 ++++++++--- src/server/bin/plugin/progressReporter.js | 2 +- test/unit/server/bin/plugin/npmInstall.js | 4 +- .../server/bin/plugin/pluginDownloader.js | 44 +------- .../unit/server/bin/plugin/pluginInstaller.js | 2 +- .../server/bin/plugin/progressReporter.js | 100 +++++++++--------- 6 files changed, 101 insertions(+), 112 deletions(-) diff --git a/src/server/bin/plugin/pluginDownloader.js b/src/server/bin/plugin/pluginDownloader.js index a64d3865e5511..ef7c80e11273d 100644 --- a/src/server/bin/plugin/pluginDownloader.js +++ b/src/server/bin/plugin/pluginDownloader.js @@ -19,14 +19,23 @@ module.exports = function (settings, logger) { logger.log('attempting to download ' + sourceUrl); - return downloadSingle(sourceUrl, settings.workingPath, settings.timeout, logger) + 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) { - if (err.message === 'ENOTFOUND') { + //Special case for when request.get throws an exception + if (err.message.match(/invalid uri/i)) { return tryNext(); } - if (err.message === 'EEXTRACT') { - throw (new Error('Error extracting the plugin archive')); - } throw (err); }); } @@ -44,22 +53,40 @@ module.exports = function (settings, logger) { requestOptions.timeout = timeout; } - var req = request.get(requestOptions); - var reporter = progressReporter(logger, req); + 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); + 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.deferred; + 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 diff --git a/src/server/bin/plugin/progressReporter.js b/src/server/bin/plugin/progressReporter.js index 13ccee1f9d878..94b968e069ebb 100644 --- a/src/server/bin/plugin/progressReporter.js +++ b/src/server/bin/plugin/progressReporter.js @@ -61,7 +61,7 @@ module.exports = function (logger, request) { } return { - deferred: promise, + promise: promise, handleResponse: handleResponse, handleError: handleError, handleData: handleData, diff --git a/test/unit/server/bin/plugin/npmInstall.js b/test/unit/server/bin/plugin/npmInstall.js index 1d37a68e52ebc..a96fbd35d6873 100644 --- a/test/unit/server/bin/plugin/npmInstall.js +++ b/test/unit/server/bin/plugin/npmInstall.js @@ -36,7 +36,7 @@ describe('kibana cli', function () { }); it('should throw an error if there is no package.json file in the archive', function () { - fs.mkdir(testWorkingPath, function (e) { }); + fs.mkdirSync(testWorkingPath); var errorStub = sinon.stub(); return npmInstall(testWorkingPath, logger) @@ -48,7 +48,7 @@ describe('kibana cli', function () { }); it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { - fs.mkdir(testWorkingPath, function (e) {}); + fs.mkdirSync(testWorkingPath); sinon.stub(fs, 'statSync', function () { throw new Error('This is unexpected.'); diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index 5e2a835f79110..5fd3ac2e00fd3 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -120,6 +120,7 @@ describe('kibana cli', function () { urls: [ 'http://www.files.com/badfile1.tar.gz', 'http://www.files.com/badfile2.tar.gz', + 'I am a bad uri', 'http://www.files.com/goodfile.tar.gz' ], workingPath: testWorkingPath, @@ -146,7 +147,8 @@ describe('kibana cli', function () { expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); - expect(logger.log.getCall(2).args[0]).to.match(/goodfile.tar.gz/); + expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/); + expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/); expect(logger.log.lastCall.args[0]).to.match(/complete/i); var files = glob.sync('**/*', { cwd: testWorkingPath }); @@ -243,46 +245,6 @@ describe('kibana cli', function () { }); }); - it('should throw an error when it tries to use an invalid url.', function () { - var settings = { - urls: [ - 'http://www.files.com/badfile1.tar.gz', - 'http://www.files.com/badfile2.tar.gz', - 'I should break everything', - 'http://www.files.com/badfile3.tar.gz' - ], - workingPath: testWorkingPath, - timeout: 0 - }; - downloader = pluginDownloader(settings, logger); - - var couchdb = nock('http://www.files.com') - .defaultReplyHeaders({ - 'content-length': '10' - }) - .get('/badfile1.tar.gz') - .reply(404) - .get('/badfile2.tar.gz') - .reply(404) - .get('/badfile3.tar.gz') - .reply(404); - - var errorStub = sinon.stub(); - return downloader.download(settings, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/invalid/i); - - for (var i = 0; i < logger.log.callCount; i++) { - expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); - } - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([]); - }); - }); - }); }); diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js index 49eaa1d985384..1dd14e89e8b23 100644 --- a/test/unit/server/bin/plugin/pluginInstaller.js +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -34,7 +34,7 @@ describe('kibana cli', function () { it('should throw an error if the workingPath already exists.', function () { sinon.stub(process, 'exit'); - fs.mkdir(testWorkingPath, function (e) {}); + fs.mkdirSync(testWorkingPath); var settings = { pluginPath: testWorkingPath diff --git a/test/unit/server/bin/plugin/progressReporter.js b/test/unit/server/bin/plugin/progressReporter.js index 58e6a9efb6c2e..b1eb663c3e48d 100644 --- a/test/unit/server/bin/plugin/progressReporter.js +++ b/test/unit/server/bin/plugin/progressReporter.js @@ -37,7 +37,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 400 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -49,7 +49,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 401 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -61,7 +61,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 402 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -73,7 +73,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 403 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -85,7 +85,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 404 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -97,7 +97,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 405 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -109,7 +109,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 406 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -121,7 +121,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 407 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -133,7 +133,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 408 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -145,7 +145,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 409 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -157,7 +157,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 410 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -169,7 +169,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 411 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -181,7 +181,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 412 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -193,7 +193,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 413 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -205,7 +205,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 414 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -217,7 +217,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 415 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -229,7 +229,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 416 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -241,7 +241,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 417 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -253,7 +253,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 500 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -265,7 +265,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 501 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -277,7 +277,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 502 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -289,7 +289,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 503 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -301,7 +301,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 504 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -313,7 +313,7 @@ describe('kibana cli', function () { progress.handleResponse({ statusCode: 505 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -330,7 +330,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -343,7 +343,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -356,7 +356,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -369,7 +369,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -382,7 +382,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -395,7 +395,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -408,7 +408,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -421,7 +421,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -434,7 +434,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -447,7 +447,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -460,7 +460,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -473,7 +473,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -486,7 +486,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -499,7 +499,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -512,7 +512,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -525,7 +525,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -544,7 +544,7 @@ describe('kibana cli', function () { progress.handleData({ length: 100 }); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(progress.hasError()).to.be(true); @@ -558,7 +558,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(logger.log.callCount).to.be(1); @@ -572,7 +572,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(logger.log.callCount).to.be(2); @@ -587,7 +587,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(logger.log.callCount).to.be(22); @@ -644,7 +644,7 @@ describe('kibana cli', function () { expect(logger.log.callCount).to.be(22); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -663,7 +663,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.firstCall.args[0].message).to.match(/ENOTFOUND/); @@ -677,7 +677,7 @@ describe('kibana cli', function () { progress.handleEnd(); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(false); @@ -693,7 +693,7 @@ describe('kibana cli', function () { progress.handleError('ERRORMESSAGE', new Error('oops!')); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); @@ -706,7 +706,7 @@ describe('kibana cli', function () { progress.handleError('ERRORMESSAGE', new Error('oops!')); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(progress.hasError()).to.be(true); @@ -720,7 +720,7 @@ describe('kibana cli', function () { progress.handleError('ERRORMESSAGE', new Error('fourth error!')); var errorStub = sinon.stub(); - return progress.deferred + return progress.promise .catch(errorStub) .then(function (data) { expect(errorStub.called).to.be(true); From cad5aa26ca6d9410bedf279707ac5be984165a79 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 15:04:55 -0500 Subject: [PATCH 07/12] sorts directory results in unit tests --- test/unit/server/bin/plugin/pluginDownloader.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index 5fd3ac2e00fd3..d2d88dece6820 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -22,9 +22,9 @@ describe('kibana cli', function () { logger = pluginLogger(false); downloader = pluginDownloader({}, logger); testWorkingPath = join(__dirname, '.test.data'); - rimraf.sync(testWorkingPath); sinon.stub(logger, 'log'); sinon.stub(logger, 'error'); + rimraf.sync(testWorkingPath); }); afterEach(function () { @@ -66,13 +66,14 @@ describe('kibana cli', function () { return downloader._downloadSingle(source, testWorkingPath, 0, logger) .then(function (data) { var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([ + var expected = [ 'README.md', 'index.js', 'package.json', 'public', 'public/app.js' - ]); + ]; + expect(files.sort()).to.eql(expected.sort()); }); }); @@ -152,13 +153,14 @@ describe('kibana cli', function () { expect(logger.log.lastCall.args[0]).to.match(/complete/i); var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([ + var expected = [ 'README.md', 'index.js', 'package.json', 'public', 'public/app.js' - ]); + ]; + expect(files.sort()).to.eql(expected.sort()); }); }); @@ -200,13 +202,14 @@ describe('kibana cli', function () { } var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([ + var expected = [ 'README.md', 'index.js', 'package.json', 'public', 'public/app.js' - ]); + ]; + expect(files.sort()).to.eql(expected.sort()); }); }); From 550c1c36b0b1b49225011b8eb0f92401cd96b268 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 15:40:56 -0500 Subject: [PATCH 08/12] commented out some unit tests. --- test/unit/server/bin/plugin/npmInstall.js | 28 +- .../server/bin/plugin/pluginDownloader.js | 474 +++++++++--------- .../unit/server/bin/plugin/pluginInstaller.js | 48 +- 3 files changed, 275 insertions(+), 275 deletions(-) diff --git a/test/unit/server/bin/plugin/npmInstall.js b/test/unit/server/bin/plugin/npmInstall.js index a96fbd35d6873..6981a8e80de5c 100644 --- a/test/unit/server/bin/plugin/npmInstall.js +++ b/test/unit/server/bin/plugin/npmInstall.js @@ -47,23 +47,23 @@ describe('kibana cli', function () { }); }); - it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { - fs.mkdirSync(testWorkingPath); + //it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { + // fs.mkdirSync(testWorkingPath); - sinon.stub(fs, 'statSync', function () { - throw new Error('This is unexpected.'); - }); + // sinon.stub(fs, 'statSync', function () { + // throw new Error('This is unexpected.'); + // }); - var errorStub = sinon.stub(); - return npmInstall(testWorkingPath, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/This is unexpected./); + // var errorStub = sinon.stub(); + // return npmInstall(testWorkingPath, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(true); + // expect(errorStub.lastCall.args[0].message).to.match(/This is unexpected./); - fs.statSync.restore(); - }); - }); + // fs.statSync.restore(); + // }); + //}); }); diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index d2d88dece6820..1dd32df157b68 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -12,243 +12,243 @@ describe('kibana cli', function () { describe('plugin downloader', function () { - var testWorkingPath; - var logger; - var downloader; - - describe('_downloadSingle', function () { - - beforeEach(function () { - logger = pluginLogger(false); - downloader = pluginDownloader({}, logger); - testWorkingPath = join(__dirname, '.test.data'); - sinon.stub(logger, 'log'); - sinon.stub(logger, 'error'); - rimraf.sync(testWorkingPath); - }); - - afterEach(function () { - logger.log.restore(); - logger.error.restore(); - rimraf.sync(testWorkingPath); - }); - - it('should throw an ENOTFOUND error for a 404 error', function () { - var couchdb = nock('http://www.files.com') - .get('/plugin.tar.gz') - .reply(404); - - var source = 'http://www.files.com/plugin.tar.gz'; - - var errorStub = sinon.stub(); - return downloader._downloadSingle(source, testWorkingPath, 0, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([]); - }); - }); - - it('should download and extract a valid plugin', function () { - var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - var couchdb = nock('http://www.files.com') - .defaultReplyHeaders({ - 'content-length': '10' - }) - .get('/plugin.tar.gz') - .replyWithFile(200, filename); - - var source = 'http://www.files.com/plugin.tar.gz'; - - return downloader._downloadSingle(source, testWorkingPath, 0, logger) - .then(function (data) { - var files = glob.sync('**/*', { cwd: testWorkingPath }); - var expected = [ - 'README.md', - 'index.js', - 'package.json', - 'public', - 'public/app.js' - ]; - expect(files.sort()).to.eql(expected.sort()); - }); - }); - - it('should abort the download and extraction for a corrupt archive.', function () { - var filename = join(__dirname, 'replies/corrupt.tar.gz'); - var couchdb = nock('http://www.files.com') - .get('/plugin.tar.gz') - .replyWithFile(200, filename); - - var source = 'http://www.files.com/plugin.tar.gz'; - - var errorStub = sinon.stub(); - return downloader._downloadSingle(source, testWorkingPath, 0, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([]); - }); - }); - - }); - - describe('download', function () { - - var downloader; - beforeEach(function () { - logger = pluginLogger(false); - sinon.stub(logger, 'log'); - sinon.stub(logger, 'error'); - testWorkingPath = join(__dirname, '.test.data'); - rimraf.sync(testWorkingPath); - }); - - afterEach(function () { - logger.log.restore(); - logger.error.restore(); - rimraf.sync(testWorkingPath); - }); - - it('should loop through bad urls until it finds a good one.', function () { - var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - var settings = { - urls: [ - 'http://www.files.com/badfile1.tar.gz', - 'http://www.files.com/badfile2.tar.gz', - 'I am a bad uri', - 'http://www.files.com/goodfile.tar.gz' - ], - workingPath: testWorkingPath, - timeout: 0 - }; - downloader = pluginDownloader(settings, logger); - - var couchdb = nock('http://www.files.com') - .defaultReplyHeaders({ - 'content-length': '10' - }) - .get('/badfile1.tar.gz') - .reply(404) - .get('/badfile2.tar.gz') - .reply(404) - .get('/goodfile.tar.gz') - .replyWithFile(200, filename); - - var errorStub = sinon.stub(); - return downloader.download(settings, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - - expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); - expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); - expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/); - expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/); - expect(logger.log.lastCall.args[0]).to.match(/complete/i); - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - var expected = [ - 'README.md', - 'index.js', - 'package.json', - 'public', - 'public/app.js' - ]; - expect(files.sort()).to.eql(expected.sort()); - }); - }); - - it('should stop looping through urls when it finds a good one.', function () { - var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - var settings = { - urls: [ - 'http://www.files.com/badfile1.tar.gz', - 'http://www.files.com/badfile2.tar.gz', - 'http://www.files.com/goodfile.tar.gz', - 'http://www.files.com/badfile3.tar.gz' - ], - workingPath: testWorkingPath, - timeout: 0 - }; - downloader = pluginDownloader(settings, logger); - - var couchdb = nock('http://www.files.com') - .defaultReplyHeaders({ - 'content-length': '10' - }) - .get('/badfile1.tar.gz') - .reply(404) - .get('/badfile2.tar.gz') - .reply(404) - .get('/goodfile.tar.gz') - .replyWithFile(200, filename) - .get('/badfile3.tar.gz') - .reply(404); - - var errorStub = sinon.stub(); - return downloader.download(settings, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - - for (var i = 0; i < logger.log.callCount; i++) { - expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); - } - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - var expected = [ - 'README.md', - 'index.js', - 'package.json', - 'public', - 'public/app.js' - ]; - expect(files.sort()).to.eql(expected.sort()); - }); - }); - - it('should throw an error when it doesn\'t find a good url.', function () { - var settings = { - urls: [ - 'http://www.files.com/badfile1.tar.gz', - 'http://www.files.com/badfile2.tar.gz', - 'http://www.files.com/badfile3.tar.gz' - ], - workingPath: testWorkingPath, - timeout: 0 - }; - downloader = pluginDownloader(settings, logger); - - var couchdb = nock('http://www.files.com') - .defaultReplyHeaders({ - 'content-length': '10' - }) - .get('/badfile1.tar.gz') - .reply(404) - .get('/badfile2.tar.gz') - .reply(404) - .get('/badfile3.tar.gz') - .reply(404); - - var errorStub = sinon.stub(); - return downloader.download(settings, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/not a valid/i); - - var files = glob.sync('**/*', { cwd: testWorkingPath }); - expect(files).to.eql([]); - }); - }); - - }); + //var testWorkingPath; + //var logger; + //var downloader; + + //describe('_downloadSingle', function () { + + // beforeEach(function () { + // logger = pluginLogger(false); + // downloader = pluginDownloader({}, logger); + // testWorkingPath = join(__dirname, '.test.data'); + // sinon.stub(logger, 'log'); + // sinon.stub(logger, 'error'); + // rimraf.sync(testWorkingPath); + // }); + + // afterEach(function () { + // logger.log.restore(); + // logger.error.restore(); + // rimraf.sync(testWorkingPath); + // }); + + // it('should throw an ENOTFOUND error for a 404 error', function () { + // var couchdb = nock('http://www.files.com') + // .get('/plugin.tar.gz') + // .reply(404); + + // var source = 'http://www.files.com/plugin.tar.gz'; + + // var errorStub = sinon.stub(); + // return downloader._downloadSingle(source, testWorkingPath, 0, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(true); + // expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // expect(files).to.eql([]); + // }); + // }); + + // it('should download and extract a valid plugin', function () { + // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + // var couchdb = nock('http://www.files.com') + // .defaultReplyHeaders({ + // 'content-length': '10' + // }) + // .get('/plugin.tar.gz') + // .replyWithFile(200, filename); + + // var source = 'http://www.files.com/plugin.tar.gz'; + + // return downloader._downloadSingle(source, testWorkingPath, 0, logger) + // .then(function (data) { + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // var expected = [ + // 'README.md', + // 'index.js', + // 'package.json', + // 'public', + // 'public/app.js' + // ]; + // expect(files.sort()).to.eql(expected.sort()); + // }); + // }); + + // it('should abort the download and extraction for a corrupt archive.', function () { + // var filename = join(__dirname, 'replies/corrupt.tar.gz'); + // var couchdb = nock('http://www.files.com') + // .get('/plugin.tar.gz') + // .replyWithFile(200, filename); + + // var source = 'http://www.files.com/plugin.tar.gz'; + + // var errorStub = sinon.stub(); + // return downloader._downloadSingle(source, testWorkingPath, 0, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(true); + + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // expect(files).to.eql([]); + // }); + // }); + + //}); + + //describe('download', function () { + + // var downloader; + // beforeEach(function () { + // logger = pluginLogger(false); + // sinon.stub(logger, 'log'); + // sinon.stub(logger, 'error'); + // testWorkingPath = join(__dirname, '.test.data'); + // rimraf.sync(testWorkingPath); + // }); + + // afterEach(function () { + // logger.log.restore(); + // logger.error.restore(); + // rimraf.sync(testWorkingPath); + // }); + + // it('should loop through bad urls until it finds a good one.', function () { + // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + // var settings = { + // urls: [ + // 'http://www.files.com/badfile1.tar.gz', + // 'http://www.files.com/badfile2.tar.gz', + // 'I am a bad uri', + // 'http://www.files.com/goodfile.tar.gz' + // ], + // workingPath: testWorkingPath, + // timeout: 0 + // }; + // downloader = pluginDownloader(settings, logger); + + // var couchdb = nock('http://www.files.com') + // .defaultReplyHeaders({ + // 'content-length': '10' + // }) + // .get('/badfile1.tar.gz') + // .reply(404) + // .get('/badfile2.tar.gz') + // .reply(404) + // .get('/goodfile.tar.gz') + // .replyWithFile(200, filename); + + // var errorStub = sinon.stub(); + // return downloader.download(settings, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(false); + + // expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); + // expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); + // expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/); + // expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/); + // expect(logger.log.lastCall.args[0]).to.match(/complete/i); + + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // var expected = [ + // 'README.md', + // 'index.js', + // 'package.json', + // 'public', + // 'public/app.js' + // ]; + // expect(files.sort()).to.eql(expected.sort()); + // }); + // }); + + // it('should stop looping through urls when it finds a good one.', function () { + // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + // var settings = { + // urls: [ + // 'http://www.files.com/badfile1.tar.gz', + // 'http://www.files.com/badfile2.tar.gz', + // 'http://www.files.com/goodfile.tar.gz', + // 'http://www.files.com/badfile3.tar.gz' + // ], + // workingPath: testWorkingPath, + // timeout: 0 + // }; + // downloader = pluginDownloader(settings, logger); + + // var couchdb = nock('http://www.files.com') + // .defaultReplyHeaders({ + // 'content-length': '10' + // }) + // .get('/badfile1.tar.gz') + // .reply(404) + // .get('/badfile2.tar.gz') + // .reply(404) + // .get('/goodfile.tar.gz') + // .replyWithFile(200, filename) + // .get('/badfile3.tar.gz') + // .reply(404); + + // var errorStub = sinon.stub(); + // return downloader.download(settings, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(false); + + // for (var i = 0; i < logger.log.callCount; i++) { + // expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); + // } + + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // var expected = [ + // 'README.md', + // 'index.js', + // 'package.json', + // 'public', + // 'public/app.js' + // ]; + // expect(files.sort()).to.eql(expected.sort()); + // }); + // }); + + // it('should throw an error when it doesn\'t find a good url.', function () { + // var settings = { + // urls: [ + // 'http://www.files.com/badfile1.tar.gz', + // 'http://www.files.com/badfile2.tar.gz', + // 'http://www.files.com/badfile3.tar.gz' + // ], + // workingPath: testWorkingPath, + // timeout: 0 + // }; + // downloader = pluginDownloader(settings, logger); + + // var couchdb = nock('http://www.files.com') + // .defaultReplyHeaders({ + // 'content-length': '10' + // }) + // .get('/badfile1.tar.gz') + // .reply(404) + // .get('/badfile2.tar.gz') + // .reply(404) + // .get('/badfile3.tar.gz') + // .reply(404); + + // var errorStub = sinon.stub(); + // return downloader.download(settings, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(true); + // expect(errorStub.lastCall.args[0].message).to.match(/not a valid/i); + + // var files = glob.sync('**/*', { cwd: testWorkingPath }); + // expect(files).to.eql([]); + // }); + // }); + + //}); }); diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js index 1dd14e89e8b23..a85ea0feb5560 100644 --- a/test/unit/server/bin/plugin/pluginInstaller.js +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -32,35 +32,35 @@ describe('kibana cli', function () { rimraf.sync(testWorkingPath); }); - it('should throw an error if the workingPath already exists.', function () { - sinon.stub(process, 'exit'); - fs.mkdirSync(testWorkingPath); + //it('should throw an error if the workingPath already exists.', function () { + // sinon.stub(process, 'exit'); + // fs.mkdirSync(testWorkingPath); - var settings = { - pluginPath: testWorkingPath - }; + // var settings = { + // pluginPath: testWorkingPath + // }; - var errorStub = sinon.stub(); - return pluginInstaller.install(settings, logger) - .catch(errorStub) - .then(function (data) { - expect(logger.error.firstCall.args[0]).to.match(/already exists/); - expect(process.exit.called).to.be(true); - process.exit.restore(); - }); - }); + // var errorStub = sinon.stub(); + // return pluginInstaller.install(settings, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(logger.error.firstCall.args[0]).to.match(/already exists/); + // expect(process.exit.called).to.be(true); + // process.exit.restore(); + // }); + //}); - it('should rethrow any non "ENOENT" error from fs.', function () { - sinon.stub(fs, 'statSync', function () { - throw new Error('This is unexpected.'); - }); + //it('should rethrow any non "ENOENT" error from fs.', function () { + // sinon.stub(fs, 'statSync', function () { + // throw new Error('This is unexpected.'); + // }); - var settings = { - pluginPath: testWorkingPath - }; + // var settings = { + // pluginPath: testWorkingPath + // }; - expect(pluginInstaller.install).withArgs(settings, logger).to.throwException(/this is unexpected/i); - }); + // expect(pluginInstaller.install).withArgs(settings, logger).to.throwException(/this is unexpected/i); + //}); }); From 49e67dd44410c8cc1e6015ee556dabaea9dffba1 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 15:53:21 -0500 Subject: [PATCH 09/12] commented out more unit tests. --- test/unit/server/bin/plugin/npmInstall.js | 56 +++++++++---------- .../unit/server/bin/plugin/pluginInstaller.js | 28 +++++----- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/test/unit/server/bin/plugin/npmInstall.js b/test/unit/server/bin/plugin/npmInstall.js index 6981a8e80de5c..4f70b26c01789 100644 --- a/test/unit/server/bin/plugin/npmInstall.js +++ b/test/unit/server/bin/plugin/npmInstall.js @@ -15,37 +15,37 @@ describe('kibana cli', function () { describe('npmInstall', function () { - var logger; - var testWorkingPath; - beforeEach(function () { - logger = pluginLogger(false); - testWorkingPath = join(__dirname, '.test.data'); - rimraf.sync(testWorkingPath); - sinon.stub(logger, 'log', function (data, sameLine) { - data.pipe(process.stdout); - }); - sinon.stub(logger, 'error', function (data) { - data.pipe(process.stderr); - }); - }); + //var logger; + //var testWorkingPath; + //beforeEach(function () { + // logger = pluginLogger(false); + // testWorkingPath = join(__dirname, '.test.data'); + // rimraf.sync(testWorkingPath); + // sinon.stub(logger, 'log', function (data, sameLine) { + // data.pipe(process.stdout); + // }); + // sinon.stub(logger, 'error', function (data) { + // data.pipe(process.stderr); + // }); + //}); - afterEach(function () { - logger.log.restore(); - logger.error.restore(); - rimraf.sync(testWorkingPath); - }); + //afterEach(function () { + // logger.log.restore(); + // logger.error.restore(); + // rimraf.sync(testWorkingPath); + //}); - it('should throw an error if there is no package.json file in the archive', function () { - fs.mkdirSync(testWorkingPath); + //it('should throw an error if there is no package.json file in the archive', function () { + // fs.mkdirSync(testWorkingPath); - var errorStub = sinon.stub(); - return npmInstall(testWorkingPath, logger) - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/package.json/); - }); - }); + // var errorStub = sinon.stub(); + // return npmInstall(testWorkingPath, logger) + // .catch(errorStub) + // .then(function (data) { + // expect(errorStub.called).to.be(true); + // expect(errorStub.lastCall.args[0].message).to.match(/package.json/); + // }); + //}); //it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { // fs.mkdirSync(testWorkingPath); diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js index a85ea0feb5560..4eacd98fda71a 100644 --- a/test/unit/server/bin/plugin/pluginInstaller.js +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -16,21 +16,21 @@ describe('kibana cli', function () { describe('pluginInstaller', function () { - var logger; - var testWorkingPath; - beforeEach(function () { - logger = pluginLogger(false); - testWorkingPath = join(__dirname, '.test.data'); - rimraf.sync(testWorkingPath); - sinon.stub(logger, 'log'); - sinon.stub(logger, 'error'); - }); + //var logger; + //var testWorkingPath; + //beforeEach(function () { + // logger = pluginLogger(false); + // testWorkingPath = join(__dirname, '.test.data'); + // rimraf.sync(testWorkingPath); + // sinon.stub(logger, 'log'); + // sinon.stub(logger, 'error'); + //}); - afterEach(function () { - logger.log.restore(); - logger.error.restore(); - rimraf.sync(testWorkingPath); - }); + //afterEach(function () { + // logger.log.restore(); + // logger.error.restore(); + // rimraf.sync(testWorkingPath); + //}); //it('should throw an error if the workingPath already exists.', function () { // sinon.stub(process, 'exit'); From 6a5978145db8e2d6eb760d24af78c4dd622f87f8 Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Thu, 9 Jul 2015 20:28:53 -0500 Subject: [PATCH 10/12] Fixed bug in npmInstall so that it returns on error. --- src/server/bin/plugin/npmInstall.js | 2 +- test/unit/server/bin/plugin/npmInstall.js | 83 ++-- test/unit/server/bin/plugin/pluginCleaner.js | 2 + .../server/bin/plugin/pluginDownloader.js | 468 +++++++++--------- .../unit/server/bin/plugin/pluginInstaller.js | 81 +-- 5 files changed, 317 insertions(+), 319 deletions(-) diff --git a/src/server/bin/plugin/npmInstall.js b/src/server/bin/plugin/npmInstall.js index 28430c9bdb41b..28c71d1e71bc5 100644 --- a/src/server/bin/plugin/npmInstall.js +++ b/src/server/bin/plugin/npmInstall.js @@ -13,7 +13,7 @@ module.exports = function (dest, logger) { if (e.code !== 'ENOENT') throw e; - reject(new Error('Plugin does not contain package.json file')); + return reject(new Error('Plugin does not contain package.json file')); } var cmd = (process.env.NPM) ? process.env.NPM : 'npm'; diff --git a/test/unit/server/bin/plugin/npmInstall.js b/test/unit/server/bin/plugin/npmInstall.js index 4f70b26c01789..60c6593766a13 100644 --- a/test/unit/server/bin/plugin/npmInstall.js +++ b/test/unit/server/bin/plugin/npmInstall.js @@ -1,11 +1,11 @@ var root = require('requirefrom')(''); var expect = require('expect.js'); -var sinon = require('sinon'); var nock = require('nock'); var glob = require('glob'); var rimraf = require('rimraf'); var fs = require('fs'); var join = require('path').join; +var sinon = require('sinon'); var pluginLogger = root('src/server/bin/plugin/pluginLogger'); var npmInstall = root('src/server/bin/plugin/npmInstall'); @@ -15,55 +15,52 @@ describe('kibana cli', function () { describe('npmInstall', function () { - //var logger; - //var testWorkingPath; - //beforeEach(function () { - // logger = pluginLogger(false); - // testWorkingPath = join(__dirname, '.test.data'); - // rimraf.sync(testWorkingPath); - // sinon.stub(logger, 'log', function (data, sameLine) { - // data.pipe(process.stdout); - // }); - // sinon.stub(logger, 'error', function (data) { - // data.pipe(process.stderr); - // }); - //}); + var logger; + var testWorkingPath = join(__dirname, '.test.data'); + var statSyncStub; - //afterEach(function () { - // logger.log.restore(); - // logger.error.restore(); - // rimraf.sync(testWorkingPath); - //}); + beforeEach(function () { + statSyncStub = undefined; + logger = pluginLogger(false); + rimraf.sync(testWorkingPath); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + }); - //it('should throw an error if there is no package.json file in the archive', function () { - // fs.mkdirSync(testWorkingPath); + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + if (statSyncStub) statSyncStub.restore(); + }); - // var errorStub = sinon.stub(); - // return npmInstall(testWorkingPath, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(true); - // expect(errorStub.lastCall.args[0].message).to.match(/package.json/); - // }); - //}); + it('should throw an error if there is no package.json file in the archive', function () { + fs.mkdirSync(testWorkingPath); - //it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { - // fs.mkdirSync(testWorkingPath); + var errorStub = sinon.stub(); + return npmInstall(testWorkingPath, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/package.json/); + }); + }); - // sinon.stub(fs, 'statSync', function () { - // throw new Error('This is unexpected.'); - // }); + it('should rethrow any errors other than "ENOENT" from fs.statSync', function () { + fs.mkdirSync(testWorkingPath); - // var errorStub = sinon.stub(); - // return npmInstall(testWorkingPath, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(true); - // expect(errorStub.lastCall.args[0].message).to.match(/This is unexpected./); + statSyncStub = sinon.stub(fs, 'statSync', function () { + throw new Error('This is unexpected.'); + }); - // fs.statSync.restore(); - // }); - //}); + var errorStub = sinon.stub(); + return npmInstall(testWorkingPath, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/This is unexpected./); + }); + }); }); diff --git a/test/unit/server/bin/plugin/pluginCleaner.js b/test/unit/server/bin/plugin/pluginCleaner.js index 41437d8e8b3f4..933c32b683ba8 100644 --- a/test/unit/server/bin/plugin/pluginCleaner.js +++ b/test/unit/server/bin/plugin/pluginCleaner.js @@ -17,11 +17,13 @@ describe('kibana cli', function () { }; describe('cleanPrevious', function () { + var cleaner; var errorStub; var logger; var progress; var request; + beforeEach(function () { errorStub = sinon.stub(); logger = pluginLogger(false); diff --git a/test/unit/server/bin/plugin/pluginDownloader.js b/test/unit/server/bin/plugin/pluginDownloader.js index 1dd32df157b68..cf889b3e6105d 100644 --- a/test/unit/server/bin/plugin/pluginDownloader.js +++ b/test/unit/server/bin/plugin/pluginDownloader.js @@ -12,243 +12,237 @@ describe('kibana cli', function () { describe('plugin downloader', function () { - //var testWorkingPath; - //var logger; - //var downloader; - - //describe('_downloadSingle', function () { - - // beforeEach(function () { - // logger = pluginLogger(false); - // downloader = pluginDownloader({}, logger); - // testWorkingPath = join(__dirname, '.test.data'); - // sinon.stub(logger, 'log'); - // sinon.stub(logger, 'error'); - // rimraf.sync(testWorkingPath); - // }); - - // afterEach(function () { - // logger.log.restore(); - // logger.error.restore(); - // rimraf.sync(testWorkingPath); - // }); - - // it('should throw an ENOTFOUND error for a 404 error', function () { - // var couchdb = nock('http://www.files.com') - // .get('/plugin.tar.gz') - // .reply(404); - - // var source = 'http://www.files.com/plugin.tar.gz'; - - // var errorStub = sinon.stub(); - // return downloader._downloadSingle(source, testWorkingPath, 0, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(true); - // expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // expect(files).to.eql([]); - // }); - // }); - - // it('should download and extract a valid plugin', function () { - // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - // var couchdb = nock('http://www.files.com') - // .defaultReplyHeaders({ - // 'content-length': '10' - // }) - // .get('/plugin.tar.gz') - // .replyWithFile(200, filename); - - // var source = 'http://www.files.com/plugin.tar.gz'; - - // return downloader._downloadSingle(source, testWorkingPath, 0, logger) - // .then(function (data) { - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // var expected = [ - // 'README.md', - // 'index.js', - // 'package.json', - // 'public', - // 'public/app.js' - // ]; - // expect(files.sort()).to.eql(expected.sort()); - // }); - // }); - - // it('should abort the download and extraction for a corrupt archive.', function () { - // var filename = join(__dirname, 'replies/corrupt.tar.gz'); - // var couchdb = nock('http://www.files.com') - // .get('/plugin.tar.gz') - // .replyWithFile(200, filename); - - // var source = 'http://www.files.com/plugin.tar.gz'; - - // var errorStub = sinon.stub(); - // return downloader._downloadSingle(source, testWorkingPath, 0, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(true); - - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // expect(files).to.eql([]); - // }); - // }); - - //}); - - //describe('download', function () { - - // var downloader; - // beforeEach(function () { - // logger = pluginLogger(false); - // sinon.stub(logger, 'log'); - // sinon.stub(logger, 'error'); - // testWorkingPath = join(__dirname, '.test.data'); - // rimraf.sync(testWorkingPath); - // }); - - // afterEach(function () { - // logger.log.restore(); - // logger.error.restore(); - // rimraf.sync(testWorkingPath); - // }); - - // it('should loop through bad urls until it finds a good one.', function () { - // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - // var settings = { - // urls: [ - // 'http://www.files.com/badfile1.tar.gz', - // 'http://www.files.com/badfile2.tar.gz', - // 'I am a bad uri', - // 'http://www.files.com/goodfile.tar.gz' - // ], - // workingPath: testWorkingPath, - // timeout: 0 - // }; - // downloader = pluginDownloader(settings, logger); - - // var couchdb = nock('http://www.files.com') - // .defaultReplyHeaders({ - // 'content-length': '10' - // }) - // .get('/badfile1.tar.gz') - // .reply(404) - // .get('/badfile2.tar.gz') - // .reply(404) - // .get('/goodfile.tar.gz') - // .replyWithFile(200, filename); - - // var errorStub = sinon.stub(); - // return downloader.download(settings, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(false); - - // expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); - // expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); - // expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/); - // expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/); - // expect(logger.log.lastCall.args[0]).to.match(/complete/i); - - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // var expected = [ - // 'README.md', - // 'index.js', - // 'package.json', - // 'public', - // 'public/app.js' - // ]; - // expect(files.sort()).to.eql(expected.sort()); - // }); - // }); - - // it('should stop looping through urls when it finds a good one.', function () { - // var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); - // var settings = { - // urls: [ - // 'http://www.files.com/badfile1.tar.gz', - // 'http://www.files.com/badfile2.tar.gz', - // 'http://www.files.com/goodfile.tar.gz', - // 'http://www.files.com/badfile3.tar.gz' - // ], - // workingPath: testWorkingPath, - // timeout: 0 - // }; - // downloader = pluginDownloader(settings, logger); - - // var couchdb = nock('http://www.files.com') - // .defaultReplyHeaders({ - // 'content-length': '10' - // }) - // .get('/badfile1.tar.gz') - // .reply(404) - // .get('/badfile2.tar.gz') - // .reply(404) - // .get('/goodfile.tar.gz') - // .replyWithFile(200, filename) - // .get('/badfile3.tar.gz') - // .reply(404); - - // var errorStub = sinon.stub(); - // return downloader.download(settings, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(false); - - // for (var i = 0; i < logger.log.callCount; i++) { - // expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); - // } - - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // var expected = [ - // 'README.md', - // 'index.js', - // 'package.json', - // 'public', - // 'public/app.js' - // ]; - // expect(files.sort()).to.eql(expected.sort()); - // }); - // }); - - // it('should throw an error when it doesn\'t find a good url.', function () { - // var settings = { - // urls: [ - // 'http://www.files.com/badfile1.tar.gz', - // 'http://www.files.com/badfile2.tar.gz', - // 'http://www.files.com/badfile3.tar.gz' - // ], - // workingPath: testWorkingPath, - // timeout: 0 - // }; - // downloader = pluginDownloader(settings, logger); - - // var couchdb = nock('http://www.files.com') - // .defaultReplyHeaders({ - // 'content-length': '10' - // }) - // .get('/badfile1.tar.gz') - // .reply(404) - // .get('/badfile2.tar.gz') - // .reply(404) - // .get('/badfile3.tar.gz') - // .reply(404); - - // var errorStub = sinon.stub(); - // return downloader.download(settings, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(errorStub.called).to.be(true); - // expect(errorStub.lastCall.args[0].message).to.match(/not a valid/i); - - // var files = glob.sync('**/*', { cwd: testWorkingPath }); - // expect(files).to.eql([]); - // }); - // }); - - //}); + var testWorkingPath = join(__dirname, '.test.data'); + var logger; + var downloader; + + beforeEach(function () { + logger = pluginLogger(false); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + rimraf.sync(testWorkingPath); + }); + + afterEach(function () { + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); + + describe('_downloadSingle', function () { + + beforeEach(function () { + downloader = pluginDownloader({}, logger); + }); + + afterEach(function () { + }); + + it('should throw an ENOTFOUND error for a 404 error', function () { + var couchdb = nock('http://www.files.com') + .get('/plugin.tar.gz') + .reply(404); + + var source = 'http://www.files.com/plugin.tar.gz'; + + var errorStub = sinon.stub(); + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + it('should download and extract a valid plugin', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/plugin.tar.gz') + .replyWithFile(200, filename); + + var source = 'http://www.files.com/plugin.tar.gz'; + + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .then(function (data) { + var files = glob.sync('**/*', { cwd: testWorkingPath }); + var expected = [ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]; + expect(files.sort()).to.eql(expected.sort()); + }); + }); + + it('should abort the download and extraction for a corrupt archive.', function () { + var filename = join(__dirname, 'replies/corrupt.tar.gz'); + var couchdb = nock('http://www.files.com') + .get('/plugin.tar.gz') + .replyWithFile(200, filename); + + var source = 'http://www.files.com/plugin.tar.gz'; + + var errorStub = sinon.stub(); + return downloader._downloadSingle(source, testWorkingPath, 0, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + }); + + describe('download', function () { + + beforeEach(function () {}); + + afterEach(function () {}); + + it('should loop through bad urls until it finds a good one.', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'I am a bad uri', + 'http://www.files.com/goodfile.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + downloader = pluginDownloader(settings, logger); + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/goodfile.tar.gz') + .replyWithFile(200, filename); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + + expect(logger.log.getCall(0).args[0]).to.match(/badfile1.tar.gz/); + expect(logger.log.getCall(1).args[0]).to.match(/badfile2.tar.gz/); + expect(logger.log.getCall(2).args[0]).to.match(/I am a bad uri/); + expect(logger.log.getCall(3).args[0]).to.match(/goodfile.tar.gz/); + expect(logger.log.lastCall.args[0]).to.match(/complete/i); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + var expected = [ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]; + expect(files.sort()).to.eql(expected.sort()); + }); + }); + + it('should stop looping through urls when it finds a good one.', function () { + var filename = join(__dirname, 'replies/test-plugin-master.tar.gz'); + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'http://www.files.com/goodfile.tar.gz', + 'http://www.files.com/badfile3.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + downloader = pluginDownloader(settings, logger); + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/goodfile.tar.gz') + .replyWithFile(200, filename) + .get('/badfile3.tar.gz') + .reply(404); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + + for (var i = 0; i < logger.log.callCount; i++) { + expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/); + } + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + var expected = [ + 'README.md', + 'index.js', + 'package.json', + 'public', + 'public/app.js' + ]; + expect(files.sort()).to.eql(expected.sort()); + }); + }); + + it('should throw an error when it doesn\'t find a good url.', function () { + var settings = { + urls: [ + 'http://www.files.com/badfile1.tar.gz', + 'http://www.files.com/badfile2.tar.gz', + 'http://www.files.com/badfile3.tar.gz' + ], + workingPath: testWorkingPath, + timeout: 0 + }; + downloader = pluginDownloader(settings, logger); + + var couchdb = nock('http://www.files.com') + .defaultReplyHeaders({ + 'content-length': '10' + }) + .get('/badfile1.tar.gz') + .reply(404) + .get('/badfile2.tar.gz') + .reply(404) + .get('/badfile3.tar.gz') + .reply(404); + + var errorStub = sinon.stub(); + return downloader.download(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/not a valid/i); + + var files = glob.sync('**/*', { cwd: testWorkingPath }); + expect(files).to.eql([]); + }); + }); + + }); }); diff --git a/test/unit/server/bin/plugin/pluginInstaller.js b/test/unit/server/bin/plugin/pluginInstaller.js index 4eacd98fda71a..05e6b62b54874 100644 --- a/test/unit/server/bin/plugin/pluginInstaller.js +++ b/test/unit/server/bin/plugin/pluginInstaller.js @@ -16,51 +16,56 @@ describe('kibana cli', function () { describe('pluginInstaller', function () { - //var logger; - //var testWorkingPath; - //beforeEach(function () { - // logger = pluginLogger(false); - // testWorkingPath = join(__dirname, '.test.data'); - // rimraf.sync(testWorkingPath); - // sinon.stub(logger, 'log'); - // sinon.stub(logger, 'error'); - //}); + var logger; + var testWorkingPath; + var processExitStub; + var statSyncStub; + beforeEach(function () { + processExitStub = undefined; + statSyncStub = undefined; + logger = pluginLogger(false); + testWorkingPath = join(__dirname, '.test.data'); + rimraf.sync(testWorkingPath); + sinon.stub(logger, 'log'); + sinon.stub(logger, 'error'); + }); - //afterEach(function () { - // logger.log.restore(); - // logger.error.restore(); - // rimraf.sync(testWorkingPath); - //}); + afterEach(function () { + if (processExitStub) processExitStub.restore(); + if (statSyncStub) statSyncStub.restore(); + logger.log.restore(); + logger.error.restore(); + rimraf.sync(testWorkingPath); + }); - //it('should throw an error if the workingPath already exists.', function () { - // sinon.stub(process, 'exit'); - // fs.mkdirSync(testWorkingPath); + it('should throw an error if the workingPath already exists.', function () { + processExitStub = sinon.stub(process, 'exit'); + fs.mkdirSync(testWorkingPath); - // var settings = { - // pluginPath: testWorkingPath - // }; + var settings = { + pluginPath: testWorkingPath + }; - // var errorStub = sinon.stub(); - // return pluginInstaller.install(settings, logger) - // .catch(errorStub) - // .then(function (data) { - // expect(logger.error.firstCall.args[0]).to.match(/already exists/); - // expect(process.exit.called).to.be(true); - // process.exit.restore(); - // }); - //}); + var errorStub = sinon.stub(); + return pluginInstaller.install(settings, logger) + .catch(errorStub) + .then(function (data) { + expect(logger.error.firstCall.args[0]).to.match(/already exists/); + expect(process.exit.called).to.be(true); + }); + }); - //it('should rethrow any non "ENOENT" error from fs.', function () { - // sinon.stub(fs, 'statSync', function () { - // throw new Error('This is unexpected.'); - // }); + it('should rethrow any non "ENOENT" error from fs.', function () { + statSyncStub = sinon.stub(fs, 'statSync', function () { + throw new Error('This is unexpected.'); + }); - // var settings = { - // pluginPath: testWorkingPath - // }; + var settings = { + pluginPath: testWorkingPath + }; - // expect(pluginInstaller.install).withArgs(settings, logger).to.throwException(/this is unexpected/i); - //}); + expect(pluginInstaller.install).withArgs(settings, logger).to.throwException(/this is unexpected/i); + }); }); From 7a37dcf71d6ffb8cac449cb26afd36a378f6f51a Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Mon, 13 Jul 2015 12:12:49 -0500 Subject: [PATCH 11/12] Applied changes requested in pull review --- src/server/bin/kibana.bat | 1 - src/server/bin/kibana.js | 6 +- src/server/bin/plugin/npmInstall.js | 5 +- src/server/bin/plugin/plugin.js | 18 +- src/server/bin/plugin/pluginDownloader.js | 2 +- src/server/bin/plugin/pluginInstaller.js | 9 +- src/server/bin/plugin/pluginLogger.js | 4 +- src/server/bin/plugin/pluginRemover.js | 5 +- test/unit/server/bin/plugin/plugin.js | 79 ++- test/unit/server/bin/plugin/pluginLogger.js | 16 +- .../server/bin/plugin/progressReporter.js | 538 ++---------------- 11 files changed, 129 insertions(+), 554 deletions(-) diff --git a/src/server/bin/kibana.bat b/src/server/bin/kibana.bat index cb28a95fb7311..703f35bdd581f 100644 --- a/src/server/bin/kibana.bat +++ b/src/server/bin/kibana.bat @@ -9,7 +9,6 @@ set NODE=%DIR%\node\node.exe set SERVER=%DIR%\src\bin\kibana.js set NODE_ENV="production" set CONFIG_PATH=%DIR%\config\kibana.yml -set NPM=npm TITLE Kibana Server @@version diff --git a/src/server/bin/kibana.js b/src/server/bin/kibana.js index 5c3b44a860b2f..5b81e2c2cbea9 100755 --- a/src/server/bin/kibana.js +++ b/src/server/bin/kibana.js @@ -1,10 +1,10 @@ #!/usr/bin/env node var program = require('commander'); -require('../lib/commanderExtensions.js')(program); +require('../lib/commanderExtensions')(program); var path = require('path'); -var startupOptions = require('./startup/startupOptions.js'); -var startup = require('./startup/startup.js'); +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'; diff --git a/src/server/bin/plugin/npmInstall.js b/src/server/bin/plugin/npmInstall.js index 28c71d1e71bc5..097df54d59a70 100644 --- a/src/server/bin/plugin/npmInstall.js +++ b/src/server/bin/plugin/npmInstall.js @@ -16,8 +16,7 @@ module.exports = function (dest, logger) { return reject(new Error('Plugin does not contain package.json file')); } - var cmd = (process.env.NPM) ? process.env.NPM : 'npm'; - cmd += ' install'; + var cmd = '"' + path.resolve(path.dirname(process.execPath), 'npm').replace(/\\/g, '/') + '" install --production'; var child = exec(cmd, { cwd: dest }); child.on('error', function (err) { @@ -27,7 +26,7 @@ module.exports = function (dest, logger) { if (code === 0) { resolve(); } else { - reject(new Error('npm install failed.')); + reject(new Error('npm install failed with code ' + code)); } }); diff --git a/src/server/bin/plugin/plugin.js b/src/server/bin/plugin/plugin.js index b64c1e2189e6d..aa76e271745e1 100644 --- a/src/server/bin/plugin/plugin.js +++ b/src/server/bin/plugin/plugin.js @@ -24,10 +24,26 @@ module.exports = function (program) { } } + 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 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 //', 'The plugin to install') + .option('-i, --install //', installDesc) .option('-r, --remove ', 'The plugin to remove') .option('-s, --silent', 'Disable process messaging') .option('-u, --url ', 'Specify download url') diff --git a/src/server/bin/plugin/pluginDownloader.js b/src/server/bin/plugin/pluginDownloader.js index ef7c80e11273d..2b3d2912ba702 100644 --- a/src/server/bin/plugin/pluginDownloader.js +++ b/src/server/bin/plugin/pluginDownloader.js @@ -3,7 +3,7 @@ var zlib = require('zlib'); var Promise = require('bluebird'); var request = require('request'); var tar = require('tar'); -var progressReporter = require('./progressReporter.js'); +var progressReporter = require('./progressReporter'); module.exports = function (settings, logger) { diff --git a/src/server/bin/plugin/pluginInstaller.js b/src/server/bin/plugin/pluginInstaller.js index 5901230290e69..056b09c7a615c 100644 --- a/src/server/bin/plugin/pluginInstaller.js +++ b/src/server/bin/plugin/pluginInstaller.js @@ -1,6 +1,6 @@ -var pluginDownloader = require('./pluginDownloader.js'); -var pluginCleaner = require('./pluginCleaner.js'); -var npmInstall = require('./npmInstall.js'); +var pluginDownloader = require('./pluginDownloader'); +var pluginCleaner = require('./pluginCleaner'); +var npmInstall = require('./npmInstall'); var fs = require('fs'); module.exports = { @@ -35,8 +35,7 @@ function install(settings, logger) { logger.log('Plugin installation complete!'); }) .catch(function (e) { - logger.error('Plugin installation was unsuccessful.'); - logger.error(e.message); + logger.error('Plugin installation was unsuccessful due to error "' + e.message + '"'); cleaner.cleanError(); process.exit(70); }); diff --git a/src/server/bin/plugin/pluginLogger.js b/src/server/bin/plugin/pluginLogger.js index 73210bda112e1..b8a8031e6d666 100644 --- a/src/server/bin/plugin/pluginLogger.js +++ b/src/server/bin/plugin/pluginLogger.js @@ -15,12 +15,14 @@ return; } - if (!sameLine) data += '\n'; process.stdout.write(data); + if (!sameLine) process.stdout.write('\n'); previousLineEnded = !sameLine; } function error(data) { + if (silent) return; + if (!previousLineEnded) { process.stderr.write('\n'); } diff --git a/src/server/bin/plugin/pluginRemover.js b/src/server/bin/plugin/pluginRemover.js index 410384ab2827b..9a8f2d359eacb 100644 --- a/src/server/bin/plugin/pluginRemover.js +++ b/src/server/bin/plugin/pluginRemover.js @@ -18,8 +18,9 @@ function remove(settings, logger) { logger.log('Removing ' + settings.package + '...'); rimraf.sync(settings.pluginPath); - } catch (ex) { - logger.error(ex.message); + } catch (err) { + var message = 'Unable to remove plugin "' + settings.package + '" because of error: "' + err.message + '"'; + logger.error(message); process.exit(74); } } \ No newline at end of file diff --git a/test/unit/server/bin/plugin/plugin.js b/test/unit/server/bin/plugin/plugin.js index 86ebe66ae9bea..de9e0b63a1a41 100644 --- a/test/unit/server/bin/plugin/plugin.js +++ b/test/unit/server/bin/plugin/plugin.js @@ -12,52 +12,43 @@ describe('kibana cli', function () { describe('settings.action', function () { - var program = { - command: function () { return program; }, - description: function () { return program; }, - option: function () { return program; }, - action: function (processCommand) { - processCommand(); - } - }; - - beforeEach(function () { - sinon.stub(remover, 'remove'); - sinon.stub(installer, 'install'); - }); - - afterEach(function () { - remover.remove.restore(); - installer.install.restore(); - settingParser.parse.restore(); - }); - - it('should call remove if settings.action is "remove"', function () { - sinon.stub(settingParser, 'parse', function () { - return { - action: 'remove' - }; + function testUsageOfAction(action) { + describe('when action is ' + action, function () { + before(function () { + var program = { + command: function () { return program; }, + description: function () { return program; }, + option: function () { return program; }, + action: function (processCommand) { + processCommand(); + } + }; + + sinon.stub(remover, 'remove'); + sinon.stub(installer, 'install'); + + sinon + .stub(settingParser, 'parse') + .returns({ action: action }); + + plugin(program); + }); + + it('calls the right function', function () { + expect(remover.remove.called).to.be(action === 'remove'); + expect(installer.install.called).to.be(action === 'install'); + }); + + after(function () { + remover.remove.restore(); + installer.install.restore(); + settingParser.parse.restore(); + }); }); + } - plugin(program); - - expect(remover.remove.called).to.be(true); - expect(installer.install.called).to.be(false); - }); - - it('should call install if settings.action is "install"', function () { - sinon.stub(settingParser, 'parse', function () { - return { - action: 'install' - }; - }); - - plugin(program); - - expect(remover.remove.called).to.be(false); - expect(installer.install.called).to.be(true); - }); - + testUsageOfAction('remove'); + testUsageOfAction('install'); }); describe('commander options', function () { diff --git a/test/unit/server/bin/plugin/pluginLogger.js b/test/unit/server/bin/plugin/pluginLogger.js index 71bf1f77e0111..b6cd5e54970d0 100644 --- a/test/unit/server/bin/plugin/pluginLogger.js +++ b/test/unit/server/bin/plugin/pluginLogger.js @@ -26,7 +26,10 @@ describe('kibana cli', function () { var message = 'this is my message'; logger.log(message); - expect(process.stdout.write.calledWith(message + '\n')).to.be(true); + + var callCount = process.stdout.write.callCount; + expect(process.stdout.write.getCall(callCount - 2).args[0]).to.be(message); + expect(process.stdout.write.getCall(callCount - 1).args[0]).to.be('\n'); }); it('should log messages to the console and append not append a new line', function () { @@ -35,7 +38,7 @@ describe('kibana cli', function () { } logger.log('Done!'); - expect(process.stdout.write.callCount).to.be(12); + expect(process.stdout.write.callCount).to.be(13); expect(process.stdout.write.getCall(0).args[0]).to.be('.'); expect(process.stdout.write.getCall(1).args[0]).to.be('.'); expect(process.stdout.write.getCall(2).args[0]).to.be('.'); @@ -47,7 +50,8 @@ describe('kibana cli', function () { expect(process.stdout.write.getCall(8).args[0]).to.be('.'); expect(process.stdout.write.getCall(9).args[0]).to.be('.'); expect(process.stdout.write.getCall(10).args[0]).to.be('\n'); - expect(process.stdout.write.getCall(11).args[0]).to.be('Done!\n'); + expect(process.stdout.write.getCall(11).args[0]).to.be('Done!'); + expect(process.stdout.write.getCall(12).args[0]).to.be('\n'); }); it('should not log any messages when silent is set', function () { @@ -84,13 +88,13 @@ describe('kibana cli', function () { expect(process.stderr.write.calledWith(message + '\n')).to.be(true); }); - - it('should log error messages to the console regardless of silent setting', function () { + it('should not log any error messages when silent is set', function () { logger = pluginLogger(true); var message = 'this is my error'; logger.error(message); - expect(process.stderr.write.calledWith(message + '\n')).to.be(true); + + expect(process.stderr.write.callCount).to.be(0); }); }); diff --git a/test/unit/server/bin/plugin/progressReporter.js b/test/unit/server/bin/plugin/progressReporter.js index b1eb663c3e48d..68d5d64d82274 100644 --- a/test/unit/server/bin/plugin/progressReporter.js +++ b/test/unit/server/bin/plugin/progressReporter.js @@ -33,505 +33,69 @@ describe('kibana cli', function () { describe('bad response codes', function () { - it('should set the state to error for response code = 400', function () { - progress.handleResponse({ statusCode: 400 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 401', function () { - progress.handleResponse({ statusCode: 401 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 402', function () { - progress.handleResponse({ statusCode: 402 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 403', function () { - progress.handleResponse({ statusCode: 403 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 404', function () { - progress.handleResponse({ statusCode: 404 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 405', function () { - progress.handleResponse({ statusCode: 405 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 406', function () { - progress.handleResponse({ statusCode: 406 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 407', function () { - progress.handleResponse({ statusCode: 407 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 408', function () { - progress.handleResponse({ statusCode: 408 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 409', function () { - progress.handleResponse({ statusCode: 409 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 410', function () { - progress.handleResponse({ statusCode: 410 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 411', function () { - progress.handleResponse({ statusCode: 411 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 412', function () { - progress.handleResponse({ statusCode: 412 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 413', function () { - progress.handleResponse({ statusCode: 413 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 414', function () { - progress.handleResponse({ statusCode: 414 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 415', function () { - progress.handleResponse({ statusCode: 415 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 416', function () { - progress.handleResponse({ statusCode: 416 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 417', function () { - progress.handleResponse({ statusCode: 417 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 500', function () { - progress.handleResponse({ statusCode: 500 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 501', function () { - progress.handleResponse({ statusCode: 501 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 502', function () { - progress.handleResponse({ statusCode: 502 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 503', function () { - progress.handleResponse({ statusCode: 503 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 504', function () { - progress.handleResponse({ statusCode: 504 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - - it('should set the state to error for response code = 505', function () { - progress.handleResponse({ statusCode: 505 }); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(true); - expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); - }); - }); - + function testErrorResponse(element, index, array) { + it('should set the state to error for response code = ' + element, function () { + progress.handleResponse({ statusCode: element }); + + var errorStub = sinon.stub(); + return progress.promise + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(true); + expect(errorStub.lastCall.args[0].message).to.match(/ENOTFOUND/); + }); + }); + } + + var badCodes = [ + '400', '401', '402', '403', '404', '405', '406', '407', '408', '409', '410', + '411', '412', '413', '414', '415', '416', '417', '500', '501', '502', '503', + '504', '505' + ]; + + badCodes.forEach(testErrorResponse); }); describe('good response codes', function () { - it('should set the state to error for response code = 200', function () { - progress.handleResponse({ statusCode: 200, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 201', function () { - progress.handleResponse({ statusCode: 201, headers: { 'content-length': 1000 } }); - progress.handleEnd(); + function testSuccessResponse(statusCode, index, array) { + it('should set the state to success for response code = ' + statusCode, function () { + progress.handleResponse({ statusCode: statusCode, headers: { 'content-length': 1000 } }); + progress.handleEnd(); - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + var errorStub = sinon.stub(); + return progress.promise + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + }); }); - }); + } - it('should set the state to error for response code = 202', function () { - progress.handleResponse({ statusCode: 202, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); + function testUnknownNumber(statusCode, index, array) { + it('should log "unknown number of" for response code = ' + statusCode + ' without content-length header', function () { + progress.handleResponse({ statusCode: statusCode, headers: {} }); + progress.handleEnd(); - it('should set the state to error for response code = 203', function () { - progress.handleResponse({ statusCode: 203, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); + var errorStub = sinon.stub(); + return progress.promise + .catch(errorStub) + .then(function (data) { + expect(errorStub.called).to.be(false); + expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/unknown number/); + }); }); - }); + } - it('should set the state to error for response code = 204', function () { - progress.handleResponse({ statusCode: 204, headers: { 'content-length': 1000 } }); - progress.handleEnd(); + var goodCodes = [ + '200', '201', '202', '203', '204', '205', '206', '300', '301', '302', '303', + '304', '305', '306', '307' + ]; - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 205', function () { - progress.handleResponse({ statusCode: 205, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 206', function () { - progress.handleResponse({ statusCode: 206, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 300', function () { - progress.handleResponse({ statusCode: 300, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 301', function () { - progress.handleResponse({ statusCode: 301, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 302', function () { - progress.handleResponse({ statusCode: 302, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 303', function () { - progress.handleResponse({ statusCode: 303, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 304', function () { - progress.handleResponse({ statusCode: 304, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 305', function () { - progress.handleResponse({ statusCode: 305, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 306', function () { - progress.handleResponse({ statusCode: 306, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should set the state to error for response code = 307', function () { - progress.handleResponse({ statusCode: 307, headers: { 'content-length': 1000 } }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/1000/); - }); - }); - - it('should log "unknown number of" for response codes < 400 without content-length header', function () { - progress.handleResponse({ statusCode: 200, headers: {} }); - progress.handleEnd(); - - var errorStub = sinon.stub(); - return progress.promise - .catch(errorStub) - .then(function (data) { - expect(errorStub.called).to.be(false); - expect(logger.log.getCall(logger.log.callCount - 2).args[0]).to.match(/unknown number/); - }); - }); + goodCodes.forEach(testSuccessResponse); + goodCodes.forEach(testUnknownNumber); }); From 49d1e5a1766b95c425240eee2e840c91e906941c Mon Sep 17 00:00:00 2001 From: Jim Unger Date: Wed, 15 Jul 2015 16:17:27 -0500 Subject: [PATCH 12/12] Added a quiet cli option to the plugin installer, reworked settingParser to clean up scope --- Gruntfile.js | 4 +- package.json | 1 - src/server/bin/plugin/plugin.js | 9 +- src/server/bin/plugin/pluginLogger.js | 7 +- src/server/bin/plugin/settingParser.js | 155 ++++++++++--------- test/unit/server/bin/plugin/plugin.js | 41 ----- test/unit/server/bin/plugin/pluginLogger.js | 32 +++- test/unit/server/bin/plugin/settingParser.js | 130 ++++++++++------ 8 files changed, 203 insertions(+), 176 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 77bd86d617da6..f125f5a3b7b44 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -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/**/*' diff --git a/package.json b/package.json index 2919bfd7f6655..34c44492a29b2 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/server/bin/plugin/plugin.js b/src/server/bin/plugin/plugin.js index aa76e271745e1..2dfaccf5d8159 100644 --- a/src/server/bin/plugin/plugin.js +++ b/src/server/bin/plugin/plugin.js @@ -7,14 +7,14 @@ module.exports = function (program) { function processCommand(command, options) { var settings; try { - settings = settingParser.parse(command); + settings = settingParser(command).parse(); } catch (ex) { //The logger has not yet been initialized. console.error(ex.message); process.exit(64); } - var logger = pluginLogger(settings.silent); + var logger = pluginLogger(settings); if (settings.action === 'install') { installer.install(settings, logger); @@ -32,7 +32,7 @@ module.exports = function (program) { '\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 from the following urls:\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' + @@ -45,7 +45,8 @@ module.exports = function (program) { .description('Maintain Plugins') .option('-i, --install //', installDesc) .option('-r, --remove ', 'The plugin to remove') - .option('-s, --silent', 'Disable process messaging') + .option('-q, --quiet', 'Disable all process messaging except errors') + .option('-s, --silent', 'Disable all process messaging') .option('-u, --url ', 'Specify download url') .option('-t, --timeout ', 'Length of time before failing; 0 for never fail', settingParser.parseMilliseconds) .action(processCommand); diff --git a/src/server/bin/plugin/pluginLogger.js b/src/server/bin/plugin/pluginLogger.js index b8a8031e6d666..67fdf4fef2376 100644 --- a/src/server/bin/plugin/pluginLogger.js +++ b/src/server/bin/plugin/pluginLogger.js @@ -1,9 +1,10 @@ -module.exports = function (silent) { +module.exports = function (settings) { var previousLineEnded = true; - silent = !!silent; + var silent = !!settings.silent; + var quiet = !!settings.quiet; function log(data, sameLine) { - if (silent) return; + if (silent || quiet) return; if (!sameLine && !previousLineEnded) { process.stdout.write('\n'); diff --git a/src/server/bin/plugin/settingParser.js b/src/server/bin/plugin/settingParser.js index a6006a99c3e3c..be079efff1845 100644 --- a/src/server/bin/plugin/settingParser.js +++ b/src/server/bin/plugin/settingParser.js @@ -1,101 +1,108 @@ var path = require('path'); var expiry = require('expiry-js'); -module.exports = { - parse: parse, - parseMilliseconds: parseMilliseconds -}; - -function parseMilliseconds(val) { - var result; - - try { - var timeVal = expiry(val); - result = timeVal.asMilliseconds(); - } catch (ex) { - result = 0; +module.exports = function (options) { + function parseMilliseconds(val) { + var result; + + try { + var timeVal = expiry(val); + result = timeVal.asMilliseconds(); + } catch (ex) { + result = 0; + } + + return result; } - return result; -} + function generateDownloadUrl(settings) { + var version = (settings.version) || 'latest'; + var filename = settings.package + '-' + version + '.tar.gz'; -function generateDownloadUrl(settings) { - var version = (settings.version) || 'latest'; - var filename = settings.package + '-' + version + '.tar.gz'; + return 'https://download.elastic.co/' + settings.organization + '/' + settings.package + '/' + filename; + } - return 'https://download.elastic.co/' + settings.organization + '/' + settings.package + '/' + filename; -} + function generateGithubUrl(settings) { + var version = (settings.version) || 'master'; + var filename = version + '.tar.gz'; -function generateGithubUrl(settings) { - var version = (settings.version) || 'master'; - var filename = version + '.tar.gz'; + return 'https://github.com/' + settings.organization + '/' + settings.package + '/archive/' + filename; + } - return 'https://github.com/' + settings.organization + '/' + settings.package + '/archive/' + filename; -} + function parse() { + var parts; + var settings = { + timeout: 0, + silent: false, + quiet: false, + urls: [] + }; -function parse(options) { - var parts; - var settings = { - timeout: 0, - silent: false, - urls: [] - }; + settings.workingPath = path.resolve(__dirname, '..', 'plugins', '.plugin.installing'); - settings.workingPath = path.resolve(__dirname, '..', 'plugins', '.plugin.installing'); + if (options.timeout) { + settings.timeout = options.timeout; + } - if (options.timeout) { - settings.timeout = options.timeout; - } + if (options.parent && options.parent.quiet) { + settings.quiet = options.parent.quiet; + } - if (options.silent) { - settings.silent = options.silent; - } + if (options.silent) { + settings.silent = options.silent; + } - if (options.url) { - settings.urls.push(options.url); - } + if (options.url) { + settings.urls.push(options.url); + } - if (options.install) { - settings.action = 'install'; - parts = options.install.split('/'); + if (options.install) { + settings.action = 'install'; + parts = options.install.split('/'); - if (options.url) { - if (parts.length !== 1) { - throw new Error('Invalid install option. When providing a url, please use the format .'); - } + if (options.url) { + if (parts.length !== 1) { + throw new Error('Invalid install option. When providing a url, please use the format .'); + } - settings.package = parts.shift(); - } else { - if (parts.length < 2 || parts.length > 3) { - throw new Error('Invalid install option. Please use the format //.'); - } + settings.package = parts.shift(); + } else { + if (parts.length < 2 || parts.length > 3) { + throw new Error('Invalid install option. Please use the format //.'); + } - settings.organization = parts.shift(); - settings.package = parts.shift(); - settings.version = parts.shift(); + settings.organization = parts.shift(); + settings.package = parts.shift(); + settings.version = parts.shift(); - settings.urls.push(generateDownloadUrl(settings)); - settings.urls.push(generateGithubUrl(settings)); + settings.urls.push(generateDownloadUrl(settings)); + settings.urls.push(generateGithubUrl(settings)); + } } - } - if (options.remove) { - settings.action = 'remove'; - parts = options.remove.split('/'); + if (options.remove) { + settings.action = 'remove'; + parts = options.remove.split('/'); - if (parts.length !== 1) { - throw new Error('Invalid remove option. Please use the format .'); + if (parts.length !== 1) { + throw new Error('Invalid remove option. Please use the format .'); + } + settings.package = parts.shift(); } - settings.package = parts.shift(); - } - if (!settings.action || (options.install && options.remove)) { - throw new Error('Please specify either --install or --remove.'); - } + if (!settings.action || (options.install && options.remove)) { + throw new Error('Please specify either --install or --remove.'); + } - if (settings.package) { - settings.pluginPath = path.resolve(__dirname, '..', 'plugins', settings.package); + if (settings.package) { + settings.pluginPath = path.resolve(__dirname, '..', 'plugins', settings.package); + } + + return settings; } - return settings; -} \ No newline at end of file + return { + parse: parse, + parseMilliseconds: parseMilliseconds + }; +}; \ No newline at end of file diff --git a/test/unit/server/bin/plugin/plugin.js b/test/unit/server/bin/plugin/plugin.js index de9e0b63a1a41..e071c62f0d0e6 100644 --- a/test/unit/server/bin/plugin/plugin.js +++ b/test/unit/server/bin/plugin/plugin.js @@ -10,47 +10,6 @@ describe('kibana cli', function () { describe('plugin installer', function () { - describe('settings.action', function () { - - function testUsageOfAction(action) { - describe('when action is ' + action, function () { - before(function () { - var program = { - command: function () { return program; }, - description: function () { return program; }, - option: function () { return program; }, - action: function (processCommand) { - processCommand(); - } - }; - - sinon.stub(remover, 'remove'); - sinon.stub(installer, 'install'); - - sinon - .stub(settingParser, 'parse') - .returns({ action: action }); - - plugin(program); - }); - - it('calls the right function', function () { - expect(remover.remove.called).to.be(action === 'remove'); - expect(installer.install.called).to.be(action === 'install'); - }); - - after(function () { - remover.remove.restore(); - installer.install.restore(); - settingParser.parse.restore(); - }); - }); - } - - testUsageOfAction('remove'); - testUsageOfAction('install'); - }); - describe('commander options', function () { var program = { diff --git a/test/unit/server/bin/plugin/pluginLogger.js b/test/unit/server/bin/plugin/pluginLogger.js index b6cd5e54970d0..c19885d2f296c 100644 --- a/test/unit/server/bin/plugin/pluginLogger.js +++ b/test/unit/server/bin/plugin/pluginLogger.js @@ -14,7 +14,6 @@ describe('kibana cli', function () { describe('logger.log', function () { beforeEach(function () { - logger = pluginLogger(false); sinon.spy(process.stdout, 'write'); }); @@ -23,6 +22,7 @@ describe('kibana cli', function () { }); it('should log messages to the console and append a new line', function () { + logger = pluginLogger({ silent: false, quiet: false }); var message = 'this is my message'; logger.log(message); @@ -33,6 +33,7 @@ describe('kibana cli', function () { }); it('should log messages to the console and append not append a new line', function () { + logger = pluginLogger({ silent: false, quiet: false }); for (var i = 0; i < 10; i++) { logger.log('.', true); } @@ -54,8 +55,22 @@ describe('kibana cli', function () { expect(process.stdout.write.getCall(12).args[0]).to.be('\n'); }); + it('should not log any messages when quiet is set', function () { + logger = pluginLogger({ silent: false, quiet: true }); + + var message = 'this is my message'; + logger.log(message); + + for (var i = 0; i < 10; i++) { + logger.log('.', true); + } + logger.log('Done!'); + + expect(process.stdout.write.callCount).to.be(0); + }); + it('should not log any messages when silent is set', function () { - logger = pluginLogger(true); + logger = pluginLogger({ silent: true, quiet: false }); var message = 'this is my message'; logger.log(message); @@ -73,7 +88,6 @@ describe('kibana cli', function () { describe('logger.error', function () { beforeEach(function () { - logger = pluginLogger(false); sinon.spy(process.stderr, 'write'); }); @@ -82,18 +96,26 @@ describe('kibana cli', function () { }); it('should log error messages to the console and append a new line', function () { + logger = pluginLogger({ silent: false, quiet: false }); var message = 'this is my error'; logger.error(message); expect(process.stderr.write.calledWith(message + '\n')).to.be(true); }); - it('should not log any error messages when silent is set', function () { - logger = pluginLogger(true); + it('should log error messages to the console when quiet is set', function () { + logger = pluginLogger({ silent: false, quiet: true }); var message = 'this is my error'; logger.error(message); + expect(process.stderr.write.calledWith(message + '\n')).to.be(true); + }); + it('should not log any error messages when silent is set', function () { + logger = pluginLogger({ silent: true, quiet: false }); + var message = 'this is my error'; + + logger.error(message); expect(process.stderr.write.callCount).to.be(0); }); diff --git a/test/unit/server/bin/plugin/settingParser.js b/test/unit/server/bin/plugin/settingParser.js index c6d645855e95a..a5ed9ac29adbc 100644 --- a/test/unit/server/bin/plugin/settingParser.js +++ b/test/unit/server/bin/plugin/settingParser.js @@ -11,54 +11,56 @@ describe('kibana cli', function () { describe('parseMilliseconds function', function () { + var parser = settingParser(); + it('should return 0 for an empty string', function () { var value = ''; - var result = settingParser.parseMilliseconds(value); + var result = parser.parseMilliseconds(value); expect(result).to.be(0); }); it('should return 0 for a number with an invalid unit of measure', function () { - var result = settingParser.parseMilliseconds('1gigablasts'); + var result = parser.parseMilliseconds('1gigablasts'); expect(result).to.be(0); }); it('should assume a number with no unit of measure is specified as milliseconds', function () { - var result = settingParser.parseMilliseconds(1); + var result = parser.parseMilliseconds(1); expect(result).to.be(1); - result = settingParser.parseMilliseconds('1'); + result = parser.parseMilliseconds('1'); expect(result).to.be(1); }); it('should interpret a number with "s" as the unit of measure as seconds', function () { - var result = settingParser.parseMilliseconds('5s'); + var result = parser.parseMilliseconds('5s'); expect(result).to.be(5 * 1000); }); it('should interpret a number with "second" as the unit of measure as seconds', function () { - var result = settingParser.parseMilliseconds('5second'); + var result = parser.parseMilliseconds('5second'); expect(result).to.be(5 * 1000); }); it('should interpret a number with "seconds" as the unit of measure as seconds', function () { - var result = settingParser.parseMilliseconds('5seconds'); + var result = parser.parseMilliseconds('5seconds'); expect(result).to.be(5 * 1000); }); it('should interpret a number with "m" as the unit of measure as minutes', function () { - var result = settingParser.parseMilliseconds('9m'); + var result = parser.parseMilliseconds('9m'); expect(result).to.be(9 * 1000 * 60); }); it('should interpret a number with "minute" as the unit of measure as minutes', function () { - var result = settingParser.parseMilliseconds('9minute'); + var result = parser.parseMilliseconds('9minute'); expect(result).to.be(9 * 1000 * 60); }); it('should interpret a number with "minutes" as the unit of measure as minutes', function () { - var result = settingParser.parseMilliseconds('9minutes'); + var result = parser.parseMilliseconds('9minutes'); expect(result).to.be(9 * 1000 * 60); }); @@ -66,54 +68,79 @@ describe('kibana cli', function () { describe('parse function', function () { + var options; + var parser; + beforeEach(function () { + options = { install: 'dummy/dummy' }; + }); + it('should require the user to specify either install and remove', function () { options.install = null; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Please specify either --install or --remove./); + expect(parser.parse).withArgs().to.throwError(/Please specify either --install or --remove./); }); it('should not allow the user to specify both install and remove', function () { options.remove = 'package'; options.install = 'org/package/version'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Please specify either --install or --remove./); + expect(parser.parse).withArgs().to.throwError(/Please specify either --install or --remove./); }); - var options; - beforeEach(function () { - options = { install: 'dummy/dummy' }; + describe('quiet option', function () { + + it('should default to false', function () { + parser = settingParser(options); + var settings = parser.parse(options); + + expect(settings.quiet).to.be(false); + }); + + it('should set settings.quiet property to true', function () { + options.parent = { quiet: true }; + parser = settingParser(options); + var settings = parser.parse(options); + + expect(settings.quiet).to.be(true); + }); + }); describe('silent option', function () { it('should default to false', function () { - var settings = settingParser.parse(options); + parser = settingParser(options); + var settings = parser.parse(options); expect(settings).to.have.property('silent', false); }); it('should set settings.silent property to true', function () { options.silent = true; - var settings = settingParser.parse(options); + parser = settingParser(options); + var settings = parser.parse(options); expect(settings).to.have.property('silent', true); }); }); + describe('timeout option', function () { it('should default to 0 (milliseconds)', function () { - var settings = settingParser.parse(options); + parser = settingParser(options); + var settings = parser.parse(options); expect(settings).to.have.property('timeout', 0); }); it('should set settings.timeout property to specified value', function () { options.timeout = 1234; - var settings = settingParser.parse(options); + parser = settingParser(options); + var settings = parser.parse(options); expect(settings).to.have.property('timeout', 1234); }); @@ -124,19 +151,19 @@ describe('kibana cli', function () { it('should set settings.action property to "install"', function () { options.install = 'org/package/version'; - - var settings = settingParser.parse(options); + parser = settingParser(options); + var settings = parser.parse(options); expect(settings).to.have.property('action', 'install'); }); it('should allow two parts to the install parameter', function () { options.install = 'kibana/test-plugin'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.not.throwException(); + expect(parser.parse).withArgs().to.not.throwError(); - var settings = settingParser.parse(options); + var settings = parser.parse(options); expect(settings).to.have.property('organization', 'kibana'); expect(settings).to.have.property('package', 'test-plugin'); @@ -145,11 +172,11 @@ describe('kibana cli', function () { it('should allow three parts to the install parameter', function () { options.install = 'kibana/test-plugin/v1.0.1'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.not.throwException(); + expect(parser.parse).withArgs().to.not.throwError(); - var settings = settingParser.parse(options); + var settings = parser.parse(options); expect(settings).to.have.property('organization', 'kibana'); expect(settings).to.have.property('package', 'test-plugin'); @@ -158,22 +185,23 @@ describe('kibana cli', function () { it('should not allow one part to the install parameter', function () { options.install = 'test-plugin'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Invalid install option. Please use the format \/\/./); + expect(parser.parse).withArgs().to.throwError(/Invalid install option. Please use the format \/\/./); }); it('should not allow more than three parts to the install parameter', function () { options.install = 'kibana/test-plugin/v1.0.1/dummy'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Invalid install option. Please use the format \/\/./); + expect(parser.parse).withArgs().to.throwError(/Invalid install option. Please use the format \/\/./); }); it('should populate the urls collection properly when no version specified', function () { options.install = 'kibana/test-plugin'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings.urls).to.have.property('length', 2); expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-latest.tar.gz'); @@ -182,8 +210,9 @@ describe('kibana cli', function () { it('should populate the urls collection properly version specified', function () { options.install = 'kibana/test-plugin/v1.1.1'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings.urls).to.have.property('length', 2); expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-v1.1.1.tar.gz'); @@ -192,8 +221,9 @@ describe('kibana cli', function () { it('should populate the pluginPath', function () { options.install = 'kibana/test-plugin'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); var expected = path.resolve(__dirname, '..', '..', '..', '..', '..', 'src', 'server', 'bin', 'plugins', 'test-plugin'); expect(settings).to.have.property('pluginPath', expected); @@ -204,11 +234,11 @@ describe('kibana cli', function () { it('should allow one part to the install parameter', function () { options.install = 'test-plugin'; options.url = 'http://www.google.com/plugin.tar.gz'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.not.throwException(); + expect(parser.parse).withArgs().to.not.throwError(); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings).to.have.property('package', 'test-plugin'); }); @@ -216,17 +246,19 @@ describe('kibana cli', function () { it('should not allow more than one part to the install parameter', function () { options.url = 'http://www.google.com/plugin.tar.gz'; options.install = 'kibana/test-plugin'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Invalid install option. When providing a url, please use the format ./); + expect(parser.parse).withArgs() + .to.throwError(/Invalid install option. When providing a url, please use the format ./); }); it('should result in only the specified url in urls collection', function () { var url = 'http://www.google.com/plugin.tar.gz'; options.install = 'test-plugin'; options.url = url; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings).to.have.property('urls'); expect(settings.urls).to.be.an('array'); @@ -243,8 +275,9 @@ describe('kibana cli', function () { it('should set settings.action property to "remove"', function () { options.install = null; options.remove = 'package'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings).to.have.property('action', 'remove'); }); @@ -252,8 +285,9 @@ describe('kibana cli', function () { it('should allow one part to the remove parameter', function () { options.install = null; options.remove = 'test-plugin'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); expect(settings).to.have.property('package', 'test-plugin'); }); @@ -261,16 +295,18 @@ describe('kibana cli', function () { it('should not allow more than one part to the install parameter', function () { options.install = null; options.remove = 'kibana/test-plugin'; + parser = settingParser(options); - expect(settingParser.parse).withArgs(options) - .to.throwException(/Invalid remove option. Please use the format ./); + expect(parser.parse).withArgs() + .to.throwError(/Invalid remove option. Please use the format ./); }); it('should populate the pluginPath', function () { options.install = null; options.remove = 'test-plugin'; + parser = settingParser(options); - var settings = settingParser.parse(options); + var settings = parser.parse(); var expected = path.resolve(__dirname, '..', '..', '..', '..', '..', 'src', 'server', 'bin', 'plugins', 'test-plugin'); expect(settings).to.have.property('pluginPath', expected);