[pack installer] modified install action
This commit is contained in:
parent
8f4ea5678f
commit
e50a79166a
|
@ -3,8 +3,8 @@ import sinon from 'sinon';
|
|||
import fs from 'fs';
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
import pluginCleaner from '../plugin_cleaner';
|
||||
import pluginLogger from '../plugin_logger';
|
||||
import { cleanPrevious, cleanError } from '../cleanup';
|
||||
import Logger from '../../lib/logger';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
|
@ -24,8 +24,7 @@ describe('kibana cli', function () {
|
|||
|
||||
beforeEach(function () {
|
||||
errorStub = sinon.stub();
|
||||
logger = pluginLogger(false);
|
||||
cleaner = pluginCleaner(settings, logger);
|
||||
logger = new Logger(settings);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
request = {
|
||||
|
@ -49,7 +48,7 @@ describe('kibana cli', function () {
|
|||
throw error;
|
||||
});
|
||||
|
||||
return cleaner.cleanPrevious(logger)
|
||||
return cleanPrevious(settings, logger)
|
||||
.catch(errorStub)
|
||||
.then(function (data) {
|
||||
expect(errorStub.called).to.be(false);
|
||||
|
@ -64,7 +63,7 @@ describe('kibana cli', function () {
|
|||
});
|
||||
|
||||
errorStub = sinon.stub();
|
||||
return cleaner.cleanPrevious(logger)
|
||||
return cleanPrevious(settings, logger)
|
||||
.catch(errorStub)
|
||||
.then(function () {
|
||||
expect(errorStub.called).to.be(true);
|
||||
|
@ -75,7 +74,7 @@ describe('kibana cli', function () {
|
|||
sinon.stub(rimraf, 'sync');
|
||||
sinon.stub(fs, 'statSync');
|
||||
|
||||
return cleaner.cleanPrevious(logger)
|
||||
return cleanPrevious(settings, logger)
|
||||
.catch(errorStub)
|
||||
.then(function (data) {
|
||||
expect(logger.log.calledWith('Found previous install attempt. Deleting...')).to.be(true);
|
||||
|
@ -89,7 +88,7 @@ describe('kibana cli', function () {
|
|||
});
|
||||
|
||||
errorStub = sinon.stub();
|
||||
return cleaner.cleanPrevious(logger)
|
||||
return cleanPrevious(settings, logger)
|
||||
.catch(errorStub)
|
||||
.then(function () {
|
||||
expect(errorStub.called).to.be(true);
|
||||
|
@ -100,7 +99,7 @@ describe('kibana cli', function () {
|
|||
sinon.stub(rimraf, 'sync');
|
||||
sinon.stub(fs, 'statSync');
|
||||
|
||||
return cleaner.cleanPrevious(logger)
|
||||
return cleanPrevious(settings, logger)
|
||||
.catch(errorStub)
|
||||
.then(function (data) {
|
||||
expect(errorStub.called).to.be(false);
|
||||
|
@ -110,12 +109,10 @@ describe('kibana cli', function () {
|
|||
});
|
||||
|
||||
describe('cleanError', function () {
|
||||
let cleaner;
|
||||
let logger;
|
||||
|
||||
beforeEach(function () {
|
||||
logger = pluginLogger(false);
|
||||
cleaner = pluginCleaner(settings, logger);
|
||||
logger = new Logger(settings);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
|
@ -125,7 +122,7 @@ describe('kibana cli', function () {
|
|||
it('should attempt to delete the working directory', function () {
|
||||
sinon.stub(rimraf, 'sync');
|
||||
|
||||
cleaner.cleanError();
|
||||
cleanError(settings);
|
||||
expect(rimraf.sync.calledWith(settings.workingPath)).to.be(true);
|
||||
});
|
||||
|
||||
|
@ -134,7 +131,7 @@ describe('kibana cli', function () {
|
|||
throw new Error('Something bad happened.');
|
||||
});
|
||||
|
||||
expect(cleaner.cleanError).withArgs(settings).to.not.throwError();
|
||||
expect(cleanError).withArgs(settings).to.not.throwError();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import nock from 'nock';
|
||||
import glob from 'glob-all';
|
||||
import glob from 'glob';
|
||||
import rimraf from 'rimraf';
|
||||
import mkdirp from 'mkdirp';
|
||||
import pluginLogger from '../plugin_logger';
|
||||
import pluginDownloader from '../plugin_downloader';
|
||||
import Logger from '../../lib/logger';
|
||||
import { download, _downloadSingle } from '../download';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
@ -13,8 +13,14 @@ describe('kibana cli', function () {
|
|||
describe('plugin downloader', function () {
|
||||
const testWorkingPath = join(__dirname, '.test.data');
|
||||
const tempArchiveFilePath = join(testWorkingPath, 'archive.part');
|
||||
let logger;
|
||||
let downloader;
|
||||
|
||||
const settings = {
|
||||
urls: [],
|
||||
workingPath: testWorkingPath,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
timeout: 0
|
||||
};
|
||||
const logger = new Logger(settings);
|
||||
|
||||
function expectWorkingPathEmpty() {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
|
@ -35,7 +41,6 @@ describe('kibana cli', function () {
|
|||
}
|
||||
|
||||
beforeEach(function () {
|
||||
logger = pluginLogger(false);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
rimraf.sync(testWorkingPath);
|
||||
|
@ -51,35 +56,10 @@ describe('kibana cli', function () {
|
|||
describe('_downloadSingle', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
const settings = {
|
||||
urls: [],
|
||||
workingPath: testWorkingPath,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
timeout: 0
|
||||
};
|
||||
downloader = pluginDownloader(settings, logger);
|
||||
});
|
||||
|
||||
describe('http downloader', function () {
|
||||
|
||||
it('should download an unsupported file type, but return undefined for archiveType', function () {
|
||||
const filePath = join(__dirname, 'replies/banana.jpg');
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
'content-length': '10',
|
||||
'content-type': 'image/jpeg'
|
||||
})
|
||||
.get('/banana.jpg')
|
||||
.replyWithFile(200, filePath);
|
||||
|
||||
const sourceUrl = 'http://www.files.com/banana.jpg';
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be(undefined);
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an ENOTFOUND error for a http ulr that returns 404', function () {
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.get('/plugin.tar.gz')
|
||||
|
@ -87,7 +67,7 @@ describe('kibana cli', function () {
|
|||
|
||||
const sourceUrl = 'http://www.files.com/plugin.tar.gz';
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.then(shouldReject, function (err) {
|
||||
expect(err.message).to.match(/ENOTFOUND/);
|
||||
expectWorkingPathEmpty();
|
||||
|
@ -97,54 +77,15 @@ describe('kibana cli', function () {
|
|||
it('should throw an ENOTFOUND error for an invalid url', function () {
|
||||
const sourceUrl = 'i am an invalid url';
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.then(shouldReject, function (err) {
|
||||
expect(err.message).to.match(/ENOTFOUND/);
|
||||
expectWorkingPathEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should download a tarball from a valid http url', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.tar.gz');
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
'content-length': '10',
|
||||
'content-type': 'application/x-gzip'
|
||||
})
|
||||
.get('/plugin.tar.gz')
|
||||
.replyWithFile(200, filePath);
|
||||
|
||||
const sourceUrl = 'http://www.files.com/plugin.tar.gz';
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be('.tar.gz');
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should consider .tgz files as archive type .tar.gz', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.tar.gz');
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
'content-length': '10'
|
||||
})
|
||||
.get('/plugin.tgz')
|
||||
.replyWithFile(200, filePath);
|
||||
|
||||
const sourceUrl = 'http://www.files.com/plugin.tgz';
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be('.tar.gz');
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should download a zip from a valid http url', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.zip');
|
||||
it('should download a file from a valid http url', function () {
|
||||
const filePath = join(__dirname, 'replies/banana.jpg');
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
|
@ -156,9 +97,8 @@ describe('kibana cli', function () {
|
|||
|
||||
const sourceUrl = 'http://www.files.com/plugin.zip';
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be('.zip');
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.then(function () {
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
@ -167,54 +107,23 @@ describe('kibana cli', function () {
|
|||
|
||||
describe('local file downloader', function () {
|
||||
|
||||
it('should copy an unsupported file type, but return undefined for archiveType', function () {
|
||||
const filePath = join(__dirname, 'replies/banana.jpg');
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
'content-length': '10',
|
||||
'content-type': 'image/jpeg'
|
||||
})
|
||||
.get('/banana.jpg')
|
||||
.replyWithFile(200, filePath);
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be(undefined);
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an ENOTFOUND error for an invalid local file', function () {
|
||||
const filePath = join(__dirname, 'replies/i-am-not-there.tar.gz');
|
||||
const filePath = join(__dirname, 'replies/i-am-not-there.zip');
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.then(shouldReject, function (err) {
|
||||
expect(err.message).to.match(/ENOTFOUND/);
|
||||
expectWorkingPathEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy a tarball from a valid local file', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.tar.gz');
|
||||
it('should copy a valid local file', function () {
|
||||
const filePath = join(__dirname, 'replies/banana.jpg');
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be('.tar.gz');
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy a zip from a valid local file', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.zip');
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
return downloader._downloadSingle(sourceUrl)
|
||||
.then(function (data) {
|
||||
expect(data.archiveType).to.be('.zip');
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.then(function () {
|
||||
expectWorkingPathNotEmpty();
|
||||
});
|
||||
});
|
||||
|
@ -225,19 +134,13 @@ describe('kibana cli', function () {
|
|||
|
||||
describe('download', function () {
|
||||
it('should loop through bad urls until it finds a good one.', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.tar.gz');
|
||||
const 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,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
timeout: 0
|
||||
};
|
||||
downloader = pluginDownloader(settings, logger);
|
||||
const filePath = join(__dirname, 'replies/test_plugin.zip');
|
||||
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'
|
||||
];
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
|
@ -250,8 +153,8 @@ describe('kibana cli', function () {
|
|||
.get('/goodfile.tar.gz')
|
||||
.replyWithFile(200, filePath);
|
||||
|
||||
return downloader.download(settings, logger)
|
||||
.then(function (data) {
|
||||
return download(settings, logger)
|
||||
.then(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(/I am a bad uri/);
|
||||
|
@ -261,19 +164,13 @@ describe('kibana cli', function () {
|
|||
});
|
||||
|
||||
it('should stop looping through urls when it finds a good one.', function () {
|
||||
const filePath = join(__dirname, 'replies/test_plugin_master.tar.gz');
|
||||
const 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,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
timeout: 0
|
||||
};
|
||||
downloader = pluginDownloader(settings, logger);
|
||||
const filePath = join(__dirname, 'replies/test_plugin.zip');
|
||||
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'
|
||||
];
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
|
@ -288,8 +185,8 @@ describe('kibana cli', function () {
|
|||
.get('/badfile3.tar.gz')
|
||||
.reply(404);
|
||||
|
||||
return downloader.download(settings, logger)
|
||||
.then(function (data) {
|
||||
return download(settings, logger)
|
||||
.then(function () {
|
||||
for (let i = 0; i < logger.log.callCount; i++) {
|
||||
expect(logger.log.getCall(i).args[0]).to.not.match(/badfile3.tar.gz/);
|
||||
}
|
||||
|
@ -298,17 +195,11 @@ describe('kibana cli', function () {
|
|||
});
|
||||
|
||||
it('should throw an error when it doesn\'t find a good url.', function () {
|
||||
const 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,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
timeout: 0
|
||||
};
|
||||
downloader = pluginDownloader(settings, logger);
|
||||
settings.urls = [
|
||||
'http://www.files.com/badfile1.tar.gz',
|
||||
'http://www.files.com/badfile2.tar.gz',
|
||||
'http://www.files.com/badfile3.tar.gz'
|
||||
];
|
||||
|
||||
const couchdb = nock('http://www.files.com')
|
||||
.defaultReplyHeaders({
|
||||
|
@ -321,7 +212,7 @@ describe('kibana cli', function () {
|
|||
.get('/badfile3.tar.gz')
|
||||
.reply(404);
|
||||
|
||||
return downloader.download(settings, logger)
|
||||
return download(settings, logger)
|
||||
.then(shouldReject, function (err) {
|
||||
expect(err.message).to.match(/no valid url specified/i);
|
||||
expectWorkingPathEmpty();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import plugin from '../plugin';
|
||||
import index from '../index';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
|
@ -18,8 +18,8 @@ describe('kibana cli', function () {
|
|||
it('should define the command', function () {
|
||||
sinon.spy(program, 'command');
|
||||
|
||||
plugin(program);
|
||||
expect(program.command.calledWith('plugin')).to.be(true);
|
||||
index(program);
|
||||
expect(program.command.calledWith('install <plugin/url>')).to.be(true);
|
||||
|
||||
program.command.restore();
|
||||
});
|
||||
|
@ -27,8 +27,8 @@ describe('kibana cli', function () {
|
|||
it('should define the description', function () {
|
||||
sinon.spy(program, 'description');
|
||||
|
||||
plugin(program);
|
||||
expect(program.description.calledWith('Maintain Plugins')).to.be(true);
|
||||
index(program);
|
||||
expect(program.description.calledWith('Install a plugin')).to.be(true);
|
||||
|
||||
program.description.restore();
|
||||
});
|
||||
|
@ -37,14 +37,14 @@ describe('kibana cli', function () {
|
|||
const spy = sinon.spy(program, 'option');
|
||||
|
||||
const options = [
|
||||
/-i/,
|
||||
/-r/,
|
||||
/-q/,
|
||||
/-s/,
|
||||
/-u/,
|
||||
/-t/
|
||||
/-c/,
|
||||
/-t/,
|
||||
/-d/
|
||||
];
|
||||
|
||||
plugin(program);
|
||||
index(program);
|
||||
|
||||
for (let i = 0; i < spy.callCount; i++) {
|
||||
const call = spy.getCall(i);
|
||||
|
@ -63,7 +63,7 @@ describe('kibana cli', function () {
|
|||
it('should call the action function', function () {
|
||||
sinon.spy(program, 'action');
|
||||
|
||||
plugin(program);
|
||||
index(program);
|
||||
expect(program.action.calledOnce).to.be(true);
|
||||
|
||||
program.action.restore();
|
||||
|
|
|
@ -1,51 +1,104 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import rimraf from 'rimraf';
|
||||
import pluginLogger from '../plugin_logger';
|
||||
import pluginInstaller from '../plugin_installer';
|
||||
import { mkdirSync } from 'fs';
|
||||
import Logger from '../../lib/logger';
|
||||
import { join } from 'path';
|
||||
import rimraf from 'rimraf';
|
||||
import mkdirp from 'mkdirp';
|
||||
import { existingInstall, checkVersion } from '../kibana';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
describe('plugin installer', function () {
|
||||
|
||||
describe('pluginInstaller', function () {
|
||||
let logger;
|
||||
let testWorkingPath;
|
||||
let processExitStub;
|
||||
describe('kibana', function () {
|
||||
const testWorkingPath = join(__dirname, '.test.data');
|
||||
const tempArchiveFilePath = join(testWorkingPath, 'archive.part');
|
||||
|
||||
beforeEach(function () {
|
||||
processExitStub = undefined;
|
||||
logger = pluginLogger(false);
|
||||
testWorkingPath = join(__dirname, '.test.data');
|
||||
rimraf.sync(testWorkingPath);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
const settings = {
|
||||
workingPath: testWorkingPath,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
plugin: 'test-plugin',
|
||||
version: '1.0.0',
|
||||
plugins: [ { name: 'foo', path: join(testWorkingPath, 'foo') } ]
|
||||
};
|
||||
|
||||
const logger = new Logger(settings);
|
||||
|
||||
describe('checkVersion', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
rimraf.sync(testWorkingPath);
|
||||
mkdirp.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 plugin does contain a version.', function () {
|
||||
const errorStub = sinon.stub();
|
||||
|
||||
try {
|
||||
checkVersion(settings);
|
||||
}
|
||||
catch (err) {
|
||||
errorStub(err);
|
||||
}
|
||||
|
||||
expect(errorStub.firstCall.args[0]).to.match(/plugin version not found/i);
|
||||
});
|
||||
|
||||
it('should throw an error if plugin version does does not match kibana version', function () {
|
||||
const errorStub = sinon.stub();
|
||||
settings.plugins[0].version = '1.2.3.4';
|
||||
|
||||
try {
|
||||
checkVersion(settings);
|
||||
}
|
||||
catch (err) {
|
||||
errorStub(err);
|
||||
}
|
||||
|
||||
expect(errorStub.firstCall.args[0]).to.match(/incorrect version/i);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (processExitStub) processExitStub.restore();
|
||||
logger.log.restore();
|
||||
logger.error.restore();
|
||||
rimraf.sync(testWorkingPath);
|
||||
});
|
||||
describe('existingInstall', function () {
|
||||
let testWorkingPath;
|
||||
let processExitStub;
|
||||
|
||||
it('should throw an error if the workingPath already exists.', function () {
|
||||
processExitStub = sinon.stub(process, 'exit');
|
||||
mkdirSync(testWorkingPath);
|
||||
beforeEach(function () {
|
||||
processExitStub = sinon.stub(process, 'exit');
|
||||
testWorkingPath = join(__dirname, '.test.data');
|
||||
rimraf.sync(testWorkingPath);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
});
|
||||
|
||||
let settings = {
|
||||
pluginPath: testWorkingPath
|
||||
};
|
||||
afterEach(function () {
|
||||
processExitStub.restore();
|
||||
logger.log.restore();
|
||||
logger.error.restore();
|
||||
rimraf.sync(testWorkingPath);
|
||||
});
|
||||
|
||||
it('should throw an error if the workingPath already exists.', function () {
|
||||
mkdirp.sync(settings.plugins[0].path);
|
||||
existingInstall(settings, logger);
|
||||
|
||||
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 not throw an error if the workingPath does not exist.', function () {
|
||||
existingInstall(settings, logger);
|
||||
expect(logger.error.called).to.be(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
164
src/cli_plugin/install/__tests__/pack.js
Normal file
164
src/cli_plugin/install/__tests__/pack.js
Normal file
|
@ -0,0 +1,164 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import glob from 'glob';
|
||||
import rimraf from 'rimraf';
|
||||
import mkdirp from 'mkdirp';
|
||||
import Logger from '../../lib/logger';
|
||||
import { extract, getPackData } from '../pack';
|
||||
import { _downloadSingle } from '../download';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
describe('plugin extractor', function () {
|
||||
|
||||
const testWorkingPath = join(__dirname, '.test.data');
|
||||
const tempArchiveFilePath = join(testWorkingPath, 'archive.part');
|
||||
const testPluginPath = join(testWorkingPath, '.installedPlugins');
|
||||
let logger;
|
||||
|
||||
const settings = {
|
||||
workingPath: testWorkingPath,
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
pluginDir: testPluginPath,
|
||||
plugin: 'test-plugin'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
logger = new Logger(settings);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
rimraf.sync(testWorkingPath);
|
||||
mkdirp.sync(testWorkingPath);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
logger.log.restore();
|
||||
logger.error.restore();
|
||||
rimraf.sync(testWorkingPath);
|
||||
});
|
||||
|
||||
function copyReplyFile(filename) {
|
||||
const filePath = join(__dirname, 'replies', filename);
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
return _downloadSingle(settings, logger, sourceUrl);
|
||||
}
|
||||
|
||||
function shouldReject() {
|
||||
throw new Error('expected the promise to reject');
|
||||
}
|
||||
|
||||
describe('extract', function () {
|
||||
|
||||
//Also only extracts the content from the kibana folder.
|
||||
//Ignores the others.
|
||||
it('successfully extract a valid zip', function () {
|
||||
return copyReplyFile('test_plugin.zip')
|
||||
.then(() => {
|
||||
return getPackData(settings, logger);
|
||||
})
|
||||
.then(() => {
|
||||
return extract(settings, logger);
|
||||
})
|
||||
.then(() => {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
const expected = [
|
||||
'archive.part',
|
||||
'README.md',
|
||||
'index.js',
|
||||
'package.json',
|
||||
'public',
|
||||
'public/app.js',
|
||||
'extra file only in zip.txt'
|
||||
];
|
||||
expect(files.sort()).to.eql(expected.sort());
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getPackData', function () {
|
||||
|
||||
it('populate settings.plugins', function () {
|
||||
return copyReplyFile('test_plugin.zip')
|
||||
.then(() => {
|
||||
return getPackData(settings, logger);
|
||||
})
|
||||
.then(() => {
|
||||
expect(settings.plugins[0].name).to.be('test-plugin');
|
||||
expect(settings.plugins[0].folder).to.be('test-plugin');
|
||||
expect(settings.plugins[0].version).to.be('1.0.0');
|
||||
expect(settings.plugins[0].platform).to.be(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('populate settings.plugins with multiple plugins', function () {
|
||||
return copyReplyFile('test_plugin_many.zip')
|
||||
.then(() => {
|
||||
return getPackData(settings, logger);
|
||||
})
|
||||
.then(() => {
|
||||
expect(settings.plugins[0].name).to.be('funger-plugin');
|
||||
expect(settings.plugins[0].file).to.be('kibana/funger-plugin/package.json');
|
||||
expect(settings.plugins[0].folder).to.be('funger-plugin');
|
||||
expect(settings.plugins[0].version).to.be('1.0.0');
|
||||
expect(settings.plugins[0].platform).to.be(undefined);
|
||||
|
||||
expect(settings.plugins[1].name).to.be('pdf');
|
||||
expect(settings.plugins[1].file).to.be('kibana/pdf-linux/package.json');
|
||||
expect(settings.plugins[1].folder).to.be('pdf-linux');
|
||||
expect(settings.plugins[1].version).to.be('1.0.0');
|
||||
expect(settings.plugins[1].platform).to.be('linux');
|
||||
|
||||
expect(settings.plugins[2].name).to.be('pdf');
|
||||
expect(settings.plugins[2].file).to.be('kibana/pdf-win32/package.json');
|
||||
expect(settings.plugins[2].folder).to.be('pdf-win32');
|
||||
expect(settings.plugins[2].version).to.be('1.0.0');
|
||||
expect(settings.plugins[2].platform).to.be('win32');
|
||||
|
||||
expect(settings.plugins[3].name).to.be('pdf');
|
||||
expect(settings.plugins[3].file).to.be('kibana/pdf-win64/package.json');
|
||||
expect(settings.plugins[3].folder).to.be('pdf-win64');
|
||||
expect(settings.plugins[3].version).to.be('1.0.0');
|
||||
expect(settings.plugins[3].platform).to.be('win64');
|
||||
|
||||
expect(settings.plugins[4].name).to.be('pdf');
|
||||
expect(settings.plugins[4].file).to.be('kibana/pdf/package.json');
|
||||
expect(settings.plugins[4].folder).to.be('pdf');
|
||||
expect(settings.plugins[4].version).to.be('1.0.0');
|
||||
expect(settings.plugins[4].platform).to.be(undefined);
|
||||
|
||||
expect(settings.plugins[5].name).to.be('test-plugin');
|
||||
expect(settings.plugins[5].file).to.be('kibana/test-plugin/package.json');
|
||||
expect(settings.plugins[5].folder).to.be('test-plugin');
|
||||
expect(settings.plugins[5].version).to.be('1.0.0');
|
||||
expect(settings.plugins[5].platform).to.be(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error if there is no kibana plugin', function () {
|
||||
return copyReplyFile('test_plugin_no_kibana.zip')
|
||||
.then((data) => {
|
||||
return getPackData(settings, logger);
|
||||
})
|
||||
.then(shouldReject, (err) => {
|
||||
expect(err.message).to.match(/No kibana plugins found in archive/i);
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error with a corrupt zip', function () {
|
||||
return copyReplyFile('corrupt.zip')
|
||||
.then((data) => {
|
||||
return getPackData(settings, logger);
|
||||
})
|
||||
.then(shouldReject, (err) => {
|
||||
expect(err.message).to.match(/error retrieving/i);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
|
@ -1,23 +1,22 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import progressReporter from '../progress_reporter';
|
||||
import pluginLogger from '../plugin_logger';
|
||||
import Progress from '../progress';
|
||||
import Logger from '../../lib/logger';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
describe('plugin installer', function () {
|
||||
|
||||
describe('progressReporter', function () {
|
||||
|
||||
let logger;
|
||||
let progress;
|
||||
let request;
|
||||
|
||||
beforeEach(function () {
|
||||
logger = pluginLogger({ silent: false, quiet: false });
|
||||
logger = new Logger({ silent: false, quiet: false });
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
progress = progressReporter(logger);
|
||||
progress = new Progress(logger);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "test-plugin",
|
||||
}
|
Binary file not shown.
BIN
src/cli_plugin/install/__tests__/replies/test_plugin.zip
Normal file
BIN
src/cli_plugin/install/__tests__/replies/test_plugin.zip
Normal file
Binary file not shown.
BIN
src/cli_plugin/install/__tests__/replies/test_plugin_many.zip
Normal file
BIN
src/cli_plugin/install/__tests__/replies/test_plugin_many.zip
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,8 +1,8 @@
|
|||
import path from 'path';
|
||||
import expect from 'expect.js';
|
||||
|
||||
import fromRoot from '../../../utils/from_root';
|
||||
import settingParser from '../setting_parser';
|
||||
import { resolve } from 'path';
|
||||
import { parseMilliseconds, parse, getPlatform } from '../settings';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
|
@ -12,56 +12,53 @@ describe('kibana cli', function () {
|
|||
|
||||
describe('parseMilliseconds function', function () {
|
||||
|
||||
var parser = settingParser();
|
||||
|
||||
it('should return 0 for an empty string', function () {
|
||||
var value = '';
|
||||
|
||||
var result = parser.parseMilliseconds(value);
|
||||
const value = '';
|
||||
const result = parseMilliseconds(value);
|
||||
|
||||
expect(result).to.be(0);
|
||||
});
|
||||
|
||||
it('should return 0 for a number with an invalid unit of measure', function () {
|
||||
var result = parser.parseMilliseconds('1gigablasts');
|
||||
const result = parseMilliseconds('1gigablasts');
|
||||
expect(result).to.be(0);
|
||||
});
|
||||
|
||||
it('should assume a number with no unit of measure is specified as milliseconds', function () {
|
||||
var result = parser.parseMilliseconds(1);
|
||||
const result = parseMilliseconds(1);
|
||||
expect(result).to.be(1);
|
||||
|
||||
result = parser.parseMilliseconds('1');
|
||||
expect(result).to.be(1);
|
||||
const result2 = parseMilliseconds('1');
|
||||
expect(result2).to.be(1);
|
||||
});
|
||||
|
||||
it('should interpret a number with "s" as the unit of measure as seconds', function () {
|
||||
var result = parser.parseMilliseconds('5s');
|
||||
const result = 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 = parser.parseMilliseconds('5second');
|
||||
const result = 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 = parser.parseMilliseconds('5seconds');
|
||||
const result = 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 = parser.parseMilliseconds('9m');
|
||||
const result = 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 = parser.parseMilliseconds('9minute');
|
||||
const result = 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 = parser.parseMilliseconds('9minutes');
|
||||
const result = parseMilliseconds('9minutes');
|
||||
expect(result).to.be(9 * 1000 * 60);
|
||||
});
|
||||
|
||||
|
@ -69,65 +66,41 @@ describe('kibana cli', function () {
|
|||
|
||||
describe('parse function', function () {
|
||||
|
||||
var options;
|
||||
var parser;
|
||||
const command = 'plugin name';
|
||||
let options = {};
|
||||
const kbnPackage = { version: 1234 };
|
||||
beforeEach(function () {
|
||||
options = { install: 'dummy/dummy', pluginDir: fromRoot('installedPlugins') };
|
||||
options = { pluginDir: fromRoot('installedPlugins') };
|
||||
});
|
||||
|
||||
it('should require the user to specify either install, remove, or list', function () {
|
||||
options.install = null;
|
||||
parser = settingParser(options);
|
||||
describe('timeout option', function () {
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Please specify either --install, --remove, or --list./);
|
||||
});
|
||||
it('should default to 0 (milliseconds)', function () {
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
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(settings.timeout).to.be(0);
|
||||
});
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Please specify either --install, --remove, or --list./);
|
||||
});
|
||||
it('should set settings.timeout property', function () {
|
||||
options.timeout = 1234;
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
it('should not allow the user to specify both install and list', function () {
|
||||
options.list = true;
|
||||
options.install = 'org/package/version';
|
||||
parser = settingParser(options);
|
||||
expect(settings.timeout).to.be(1234);
|
||||
});
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Please specify either --install, --remove, or --list./);
|
||||
});
|
||||
|
||||
it('should not allow the user to specify both remove and list', function () {
|
||||
options.list = true;
|
||||
options.remove = 'package';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Please specify either --install, --remove, or --list./);
|
||||
});
|
||||
|
||||
it('should not allow the user to specify install, remove, and list', function () {
|
||||
options.list = true;
|
||||
options.install = 'org/package/version';
|
||||
options.remove = 'package';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Please specify either --install, --remove, or --list./);
|
||||
});
|
||||
|
||||
describe('quiet option', function () {
|
||||
|
||||
it('should default to false', function () {
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
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);
|
||||
options.quiet = true;
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings.quiet).to.be(true);
|
||||
});
|
||||
|
@ -137,238 +110,111 @@ describe('kibana cli', function () {
|
|||
describe('silent option', function () {
|
||||
|
||||
it('should default to false', function () {
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings).to.have.property('silent', false);
|
||||
expect(settings.silent).to.be(false);
|
||||
});
|
||||
|
||||
it('should set settings.silent property to true', function () {
|
||||
options.silent = true;
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings).to.have.property('silent', true);
|
||||
expect(settings.silent).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('config option', function () {
|
||||
|
||||
describe('timeout option', function () {
|
||||
it('should default to ZLS', function () {
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
it('should default to 0 (milliseconds)', function () {
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
|
||||
expect(settings).to.have.property('timeout', 0);
|
||||
expect(settings.config).to.be('');
|
||||
});
|
||||
|
||||
it('should set settings.timeout property to specified value', function () {
|
||||
options.timeout = 1234;
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
it('should set settings.config property', function () {
|
||||
options.config = 'foo bar baz';
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings).to.have.property('timeout', 1234);
|
||||
expect(settings.config).to.be('foo bar baz');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('install option', function () {
|
||||
describe('pluginDir option', function () {
|
||||
|
||||
it('should set settings.action property to "install"', function () {
|
||||
options.install = 'org/package/version';
|
||||
parser = settingParser(options);
|
||||
var settings = parser.parse(options);
|
||||
it('should default to installedPlugins', function () {
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings).to.have.property('action', 'install');
|
||||
expect(settings.pluginDir).to.be(fromRoot('installedPlugins'));
|
||||
});
|
||||
|
||||
it('should allow two parts to the install parameter', function () {
|
||||
options.install = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.not.throwError();
|
||||
|
||||
var settings = parser.parse(options);
|
||||
|
||||
expect(settings).to.have.property('organization', 'kibana');
|
||||
expect(settings).to.have.property('package', 'test-plugin');
|
||||
expect(settings).to.have.property('version', undefined);
|
||||
});
|
||||
|
||||
it('should allow three parts to the install parameter', function () {
|
||||
options.install = 'kibana/test-plugin/v1.0.1';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.not.throwError();
|
||||
|
||||
var settings = parser.parse(options);
|
||||
|
||||
expect(settings).to.have.property('organization', 'kibana');
|
||||
expect(settings).to.have.property('package', 'test-plugin');
|
||||
expect(settings).to.have.property('version', 'v1.0.1');
|
||||
});
|
||||
|
||||
it('should not allow one part to the install parameter', function () {
|
||||
options.install = 'test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.throwError(/Invalid install option. Please use the format <org>\/<plugin>\/<version>./);
|
||||
});
|
||||
|
||||
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(parser.parse).withArgs().to.throwError(/Invalid install option. Please use the format <org>\/<plugin>\/<version>./);
|
||||
});
|
||||
|
||||
it('should populate the urls collection properly when no version specified', function () {
|
||||
options.install = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
|
||||
expect(settings.urls).to.have.property('length', 1);
|
||||
expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-latest.tar.gz');
|
||||
});
|
||||
|
||||
it('should populate the urls collection properly version specified', function () {
|
||||
options.install = 'kibana/test-plugin/v1.1.1';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
|
||||
expect(settings.urls).to.have.property('length', 1);
|
||||
expect(settings.urls).to.contain('https://download.elastic.co/kibana/test-plugin/test-plugin-v1.1.1.tar.gz');
|
||||
});
|
||||
|
||||
it('should populate the pluginPath', function () {
|
||||
options.install = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
var expected = fromRoot('installedPlugins/test-plugin');
|
||||
|
||||
expect(settings).to.have.property('pluginPath', expected);
|
||||
});
|
||||
|
||||
it('should populate the workingPath', function () {
|
||||
options.install = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
var expected = fromRoot('installedPlugins/.plugin.installing');
|
||||
|
||||
expect(settings).to.have.property('workingPath', expected);
|
||||
});
|
||||
|
||||
it('should populate the tempArchiveFile', function () {
|
||||
options.install = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
var expected = fromRoot('installedPlugins/.plugin.installing/archive.part');
|
||||
|
||||
expect(settings).to.have.property('tempArchiveFile', 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';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs().to.not.throwError();
|
||||
|
||||
var settings = parser.parse();
|
||||
|
||||
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';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs()
|
||||
.to.throwError(/Invalid install option. When providing a url, please use the format <plugin>./);
|
||||
});
|
||||
|
||||
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 = parser.parse();
|
||||
|
||||
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);
|
||||
});
|
||||
it('should set settings.config property', function () {
|
||||
options.pluginDir = 'foo bar baz';
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
expect(settings.pluginDir).to.be('foo bar baz');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('remove option', function () {
|
||||
describe('command value', function () {
|
||||
|
||||
it('should set settings.action property to "remove"', function () {
|
||||
delete options.install;
|
||||
options.remove = 'package';
|
||||
parser = settingParser(options);
|
||||
it('should set settings.plugin property', function () {
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
var settings = parser.parse();
|
||||
|
||||
expect(settings).to.have.property('action', 'remove');
|
||||
});
|
||||
|
||||
it('should allow one part to the remove parameter', function () {
|
||||
delete options.install;
|
||||
options.remove = 'test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
|
||||
expect(settings).to.have.property('package', 'test-plugin');
|
||||
});
|
||||
|
||||
it('should not allow more than one part to the remove parameter', function () {
|
||||
delete options.install;
|
||||
options.remove = 'kibana/test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
expect(parser.parse).withArgs()
|
||||
.to.throwError(/Invalid remove option. Please use the format <plugin>./);
|
||||
});
|
||||
|
||||
it('should populate the pluginPath', function () {
|
||||
delete options.install;
|
||||
options.remove = 'test-plugin';
|
||||
parser = settingParser(options);
|
||||
|
||||
var settings = parser.parse();
|
||||
var expected = fromRoot('installedPlugins/test-plugin');
|
||||
|
||||
expect(settings).to.have.property('pluginPath', expected);
|
||||
expect(settings.plugin).to.be(command);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('list option', function () {
|
||||
describe('urls collection', function () {
|
||||
|
||||
it('should set settings.action property to "list"', function () {
|
||||
delete options.install;
|
||||
delete options.remove;
|
||||
options.list = true;
|
||||
parser = settingParser(options);
|
||||
it('should populate the settings.urls property', function () {
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
|
||||
var settings = parser.parse();
|
||||
const expected = [
|
||||
command,
|
||||
`https://download.elastic.co/packs/${command}/${command}-1234.zip`
|
||||
];
|
||||
|
||||
expect(settings).to.have.property('action', 'list');
|
||||
expect(settings.urls).to.eql(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('workingPath value', function () {
|
||||
|
||||
it('should set settings.workingPath property', function () {
|
||||
options.pluginDir = 'foo/bar/baz';
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
const expected = resolve('foo/bar/baz', '.plugin.installing');
|
||||
|
||||
expect(settings.workingPath).to.be(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('tempArchiveFile value', function () {
|
||||
|
||||
it('should set settings.tempArchiveFile property', function () {
|
||||
options.pluginDir = 'foo/bar/baz';
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
const expected = resolve('foo/bar/baz', '.plugin.installing', 'archive.part');
|
||||
|
||||
expect(settings.tempArchiveFile).to.be(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('tempPackageFile value', function () {
|
||||
|
||||
it('should set settings.tempPackageFile property', function () {
|
||||
options.pluginDir = 'foo/bar/baz';
|
||||
const settings = parse(command, options, kbnPackage);
|
||||
const expected = resolve('foo/bar/baz', '.plugin.installing', 'package.json');
|
||||
|
||||
expect(settings.tempPackageFile).to.be(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import glob from 'glob-all';
|
||||
import glob from 'glob';
|
||||
import rimraf from 'rimraf';
|
||||
import mkdirp from 'mkdirp';
|
||||
|
||||
import pluginLogger from '../plugin_logger';
|
||||
import extract from '../plugin_extractor';
|
||||
import pluginDownloader from '../plugin_downloader';
|
||||
import Logger from '../../lib/logger';
|
||||
import { _downloadSingle } from '../download';
|
||||
import { join } from 'path';
|
||||
import { listFiles, extractFiles } from '../zip';
|
||||
|
||||
describe('kibana cli', function () {
|
||||
|
||||
|
@ -16,11 +15,12 @@ describe('kibana cli', function () {
|
|||
const testWorkingPath = join(__dirname, '.test.data');
|
||||
const tempArchiveFilePath = join(testWorkingPath, 'archive.part');
|
||||
let logger;
|
||||
let downloader;
|
||||
|
||||
const settings = {
|
||||
workingPath: testWorkingPath,
|
||||
tempArchiveFile: tempArchiveFilePath
|
||||
tempArchiveFile: tempArchiveFilePath,
|
||||
plugin: 'test-plugin',
|
||||
setPlugin: function (plugin) {}
|
||||
};
|
||||
|
||||
function shouldReject() {
|
||||
|
@ -28,17 +28,18 @@ describe('kibana cli', function () {
|
|||
}
|
||||
|
||||
beforeEach(function () {
|
||||
logger = pluginLogger(false);
|
||||
logger = new Logger(settings);
|
||||
sinon.stub(logger, 'log');
|
||||
sinon.stub(logger, 'error');
|
||||
sinon.stub(settings, 'setPlugin');
|
||||
rimraf.sync(testWorkingPath);
|
||||
mkdirp.sync(testWorkingPath);
|
||||
downloader = pluginDownloader(settings, logger);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
logger.log.restore();
|
||||
logger.error.restore();
|
||||
settings.setPlugin.restore();
|
||||
rimraf.sync(testWorkingPath);
|
||||
});
|
||||
|
||||
|
@ -46,86 +47,99 @@ describe('kibana cli', function () {
|
|||
const filePath = join(__dirname, 'replies', filename);
|
||||
const sourceUrl = 'file://' + filePath.replace(/\\/g, '/');
|
||||
|
||||
return downloader._downloadSingle(sourceUrl);
|
||||
return _downloadSingle(settings, logger, sourceUrl);
|
||||
}
|
||||
|
||||
function shouldReject() {
|
||||
throw new Error('expected the promise to reject');
|
||||
}
|
||||
describe('listFiles', function () {
|
||||
|
||||
describe('extractArchive', function () {
|
||||
|
||||
it('successfully extract a valid tarball', function () {
|
||||
return copyReplyFile('test_plugin_master.tar.gz')
|
||||
.then((data) => {
|
||||
return extract(settings, logger, data.archiveType);
|
||||
})
|
||||
it('lists the files in the zip', function () {
|
||||
return copyReplyFile('test_plugin.zip')
|
||||
.then(() => {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
return listFiles(settings.tempArchiveFile);
|
||||
})
|
||||
.then((actual) => {
|
||||
const expected = [
|
||||
'archive.part',
|
||||
'README.md',
|
||||
'index.js',
|
||||
'package.json',
|
||||
'public',
|
||||
'public/app.js'
|
||||
'elasticsearch\\',
|
||||
'kibana\\',
|
||||
'kibana\\test-plugin\\',
|
||||
'kibana\\test-plugin\\.gitignore',
|
||||
'kibana\\test-plugin\\extra file only in zip.txt',
|
||||
'kibana\\test-plugin\\index.js',
|
||||
'kibana\\test-plugin\\package.json',
|
||||
'kibana\\test-plugin\\public\\',
|
||||
'kibana\\test-plugin\\public\\app.js',
|
||||
'kibana\\test-plugin\\README.md',
|
||||
'logstash\\'
|
||||
];
|
||||
expect(files.sort()).to.eql(expected.sort());
|
||||
});
|
||||
});
|
||||
|
||||
it('successfully extract a valid zip', function () {
|
||||
return copyReplyFile('test_plugin_master.zip')
|
||||
.then((data) => {
|
||||
return extract(settings, logger, data.archiveType);
|
||||
})
|
||||
.then(() => {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
const expected = [
|
||||
'archive.part',
|
||||
'README.md',
|
||||
'index.js',
|
||||
'package.json',
|
||||
'public',
|
||||
'public/app.js',
|
||||
'extra file only in zip.txt'
|
||||
];
|
||||
expect(files.sort()).to.eql(expected.sort());
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error when extracting a corrupt zip', function () {
|
||||
return copyReplyFile('corrupt.zip')
|
||||
.then((data) => {
|
||||
return extract(settings, logger, data.archiveType);
|
||||
})
|
||||
.then(shouldReject, (err) => {
|
||||
expect(err.message).to.match(/error extracting/i);
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error when extracting a corrupt tarball', function () {
|
||||
return copyReplyFile('corrupt.tar.gz')
|
||||
.then((data) => {
|
||||
return extract(settings, logger, data.archiveType);
|
||||
})
|
||||
.then(shouldReject, (err) => {
|
||||
expect(err.message).to.match(/error extracting/i);
|
||||
});
|
||||
});
|
||||
|
||||
it('throw an error when passed an unknown archive type', function () {
|
||||
return copyReplyFile('banana.jpg')
|
||||
.then((data) => {
|
||||
return extract(settings, logger, data.archiveType);
|
||||
})
|
||||
.then(shouldReject, (err) => {
|
||||
expect(err.message).to.match(/unsupported archive format/i);
|
||||
expect(actual).to.eql(expected);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('extractFiles', function () {
|
||||
|
||||
it('extracts files using the files filter', function () {
|
||||
return copyReplyFile('test_plugin_many.zip')
|
||||
.then(() => {
|
||||
const filter = {
|
||||
files: [
|
||||
'kibana/funger-plugin/extra file only in zip.txt',
|
||||
'kibana/funger-plugin/index.js',
|
||||
'kibana\\funger-plugin\\package.json'
|
||||
]
|
||||
};
|
||||
|
||||
return extractFiles(settings.tempArchiveFile, settings.workingPath, 0, filter);
|
||||
})
|
||||
.then(() => {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
const expected = [
|
||||
'kibana',
|
||||
'kibana/funger-plugin',
|
||||
'kibana/funger-plugin/extra file only in zip.txt',
|
||||
'kibana/funger-plugin/index.js',
|
||||
'kibana/funger-plugin/package.json',
|
||||
'archive.part'
|
||||
];
|
||||
expect(files.sort()).to.eql(expected.sort());
|
||||
});
|
||||
});
|
||||
|
||||
it('extracts files using the paths filter', function () {
|
||||
return copyReplyFile('test_plugin_many.zip')
|
||||
.then(() => {
|
||||
const filter = {
|
||||
paths: [
|
||||
'kibana/funger-plugin',
|
||||
'kibana/test-plugin/public'
|
||||
]
|
||||
};
|
||||
|
||||
return extractFiles(settings.tempArchiveFile, settings.workingPath, 0, filter);
|
||||
})
|
||||
.then(() => {
|
||||
const files = glob.sync('**/*', { cwd: testWorkingPath });
|
||||
const expected = [
|
||||
'archive.part',
|
||||
'kibana',
|
||||
'kibana/funger-plugin',
|
||||
'kibana/funger-plugin/README.md',
|
||||
'kibana/funger-plugin/extra file only in zip.txt',
|
||||
'kibana/funger-plugin/index.js',
|
||||
'kibana/funger-plugin/package.json',
|
||||
'kibana/funger-plugin/public',
|
||||
'kibana/funger-plugin/public/app.js',
|
||||
'kibana/test-plugin',
|
||||
'kibana/test-plugin/public',
|
||||
'kibana/test-plugin/public/app.js'
|
||||
];
|
||||
expect(files.sort()).to.eql(expected.sort());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,39 +1,32 @@
|
|||
import rimraf from 'rimraf';
|
||||
import fs from 'fs';
|
||||
|
||||
export default function createPluginCleaner(settings, logger) {
|
||||
function cleanPrevious() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
fs.statSync(settings.workingPath);
|
||||
|
||||
logger.log('Found previous install attempt. Deleting...');
|
||||
try {
|
||||
rimraf.sync(settings.workingPath);
|
||||
} catch (e) {
|
||||
return reject(e);
|
||||
}
|
||||
return resolve();
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') return reject(e);
|
||||
|
||||
return resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function cleanError() {
|
||||
// delete the working directory.
|
||||
// At this point we're bailing, so swallow any errors on delete.
|
||||
export function cleanPrevious(settings, logger) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
rimraf.sync(settings.workingPath);
|
||||
rimraf.sync(settings.pluginPath);
|
||||
}
|
||||
catch (e) {} // eslint-disable-line no-empty
|
||||
}
|
||||
fs.statSync(settings.workingPath);
|
||||
|
||||
return {
|
||||
cleanPrevious: cleanPrevious,
|
||||
cleanError: cleanError
|
||||
};
|
||||
logger.log('Found previous install attempt. Deleting...');
|
||||
try {
|
||||
rimraf.sync(settings.workingPath);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
resolve();
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') reject(e);
|
||||
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function cleanError(settings) {
|
||||
// delete the working directory.
|
||||
// At this point we're bailing, so swallow any errors on delete.
|
||||
try {
|
||||
rimraf.sync(settings.workingPath);
|
||||
rimraf.sync(settings.plugins[0].path);
|
||||
}
|
||||
catch (e) {} // eslint-disable-line no-empty
|
||||
};
|
||||
|
|
|
@ -1,51 +1,41 @@
|
|||
import _ from 'lodash';
|
||||
import downloadHttpFile from './downloaders/http';
|
||||
import downloadLocalFile from './downloaders/file';
|
||||
import { parse as urlParse } from 'url';
|
||||
import { parse } from 'url';
|
||||
|
||||
export default function createPluginDownloader(settings, logger) {
|
||||
let archiveType;
|
||||
let sourceType;
|
||||
export function _downloadSingle(settings, logger, sourceUrl) {
|
||||
const urlInfo = parse(sourceUrl);
|
||||
let downloadPromise;
|
||||
|
||||
//Attempts to download each url in turn until one is successful
|
||||
function download() {
|
||||
const urls = settings.urls.slice(0);
|
||||
if (/^file/.test(urlInfo.protocol)) {
|
||||
downloadPromise = downloadLocalFile(logger, decodeURI(urlInfo.path), settings.tempArchiveFile);
|
||||
} else {
|
||||
downloadPromise = downloadHttpFile(logger, sourceUrl, settings.tempArchiveFile, settings.timeout);
|
||||
}
|
||||
|
||||
function tryNext() {
|
||||
const sourceUrl = urls.shift();
|
||||
if (!sourceUrl) {
|
||||
throw new Error('No valid url specified.');
|
||||
return downloadPromise;
|
||||
}
|
||||
|
||||
//Attempts to download each url in turn until one is successful
|
||||
export function download(settings, logger) {
|
||||
const urls = settings.urls.slice(0);
|
||||
|
||||
function tryNext() {
|
||||
const sourceUrl = urls.shift();
|
||||
if (!sourceUrl) {
|
||||
throw new Error('No valid url specified.');
|
||||
}
|
||||
|
||||
logger.log(`Attempting to transfer from ${sourceUrl}`);
|
||||
|
||||
return _downloadSingle(settings, logger, sourceUrl)
|
||||
.catch((err) => {
|
||||
if (err.message === 'ENOTFOUND') {
|
||||
return tryNext();
|
||||
}
|
||||
|
||||
logger.log(`Attempting to transfer from ${sourceUrl}`);
|
||||
|
||||
return downloadSingle(sourceUrl)
|
||||
.catch((err) => {
|
||||
if (err.message === 'ENOTFOUND') {
|
||||
return tryNext();
|
||||
}
|
||||
throw (err);
|
||||
});
|
||||
}
|
||||
|
||||
return tryNext();
|
||||
throw (err);
|
||||
});
|
||||
}
|
||||
|
||||
function downloadSingle(sourceUrl) {
|
||||
const urlInfo = urlParse(sourceUrl);
|
||||
let downloadPromise;
|
||||
|
||||
if (/^file/.test(urlInfo.protocol)) {
|
||||
downloadPromise = downloadLocalFile(logger, urlInfo.path, settings.tempArchiveFile);
|
||||
} else {
|
||||
downloadPromise = downloadHttpFile(logger, sourceUrl, settings.tempArchiveFile, settings.timeout);
|
||||
}
|
||||
|
||||
return downloadPromise;
|
||||
}
|
||||
|
||||
return {
|
||||
download: download,
|
||||
_downloadSingle: downloadSingle
|
||||
};
|
||||
return tryNext();
|
||||
};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import getProgressReporter from '../progress_reporter';
|
||||
import Progress from '../progress';
|
||||
import { createWriteStream, createReadStream, unlinkSync, statSync } from 'fs';
|
||||
import fileType from '../file_type';
|
||||
|
||||
function openSourceFile({ sourcePath }) {
|
||||
try {
|
||||
|
@ -18,7 +17,7 @@ function openSourceFile({ sourcePath }) {
|
|||
}
|
||||
}
|
||||
|
||||
async function copyFile({ readStream, writeStream, progressReporter }) {
|
||||
async function copyFile({ readStream, writeStream, progress }) {
|
||||
await new Promise((resolve, reject) => {
|
||||
// if either stream errors, fail quickly
|
||||
readStream.on('error', reject);
|
||||
|
@ -26,7 +25,7 @@ async function copyFile({ readStream, writeStream, progressReporter }) {
|
|||
|
||||
// report progress as we transfer
|
||||
readStream.on('data', (chunk) => {
|
||||
progressReporter.progress(chunk.length);
|
||||
progress.progress(chunk.length);
|
||||
});
|
||||
|
||||
// write the download to the file system
|
||||
|
@ -46,21 +45,17 @@ export default async function copyLocalFile(logger, sourcePath, targetPath) {
|
|||
const writeStream = createWriteStream(targetPath);
|
||||
|
||||
try {
|
||||
const progressReporter = getProgressReporter(logger);
|
||||
progressReporter.init(fileInfo.size);
|
||||
const progress = new Progress(logger);
|
||||
progress.init(fileInfo.size);
|
||||
|
||||
await copyFile({ readStream, writeStream, progressReporter });
|
||||
await copyFile({ readStream, writeStream, progress });
|
||||
|
||||
progressReporter.complete();
|
||||
progress.complete();
|
||||
} catch (err) {
|
||||
readStream.close();
|
||||
writeStream.close();
|
||||
throw err;
|
||||
}
|
||||
|
||||
// all is well, return our archive type
|
||||
const archiveType = fileType(sourcePath);
|
||||
return { archiveType };
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
throw err;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import Wreck from 'wreck';
|
||||
import getProgressReporter from '../progress_reporter';
|
||||
import Progress from '../progress';
|
||||
import { fromNode as fn } from 'bluebird';
|
||||
import { createWriteStream, unlinkSync } from 'fs';
|
||||
import fileType, { ZIP, TAR } from '../file_type';
|
||||
|
||||
function sendRequest({ sourceUrl, timeout }) {
|
||||
const maxRedirects = 11; //Because this one goes to 11.
|
||||
|
@ -25,7 +24,7 @@ function sendRequest({ sourceUrl, timeout }) {
|
|||
});
|
||||
}
|
||||
|
||||
function downloadResponse({ resp, targetPath, progressReporter }) {
|
||||
function downloadResponse({ resp, targetPath, progress }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const writeStream = createWriteStream(targetPath);
|
||||
|
||||
|
@ -35,7 +34,7 @@ function downloadResponse({ resp, targetPath, progressReporter }) {
|
|||
|
||||
// report progress as we download
|
||||
resp.on('data', (chunk) => {
|
||||
progressReporter.progress(chunk.length);
|
||||
progress.progress(chunk.length);
|
||||
});
|
||||
|
||||
// write the download to the file system
|
||||
|
@ -46,19 +45,6 @@ function downloadResponse({ resp, targetPath, progressReporter }) {
|
|||
});
|
||||
}
|
||||
|
||||
function getArchiveTypeFromResponse(resp, sourceUrl) {
|
||||
const contentType = (resp.headers['content-type'] || '');
|
||||
|
||||
switch (contentType.toLowerCase()) {
|
||||
case 'application/zip': return ZIP;
|
||||
case 'application/x-gzip': return TAR;
|
||||
default:
|
||||
//If we can't infer the archive type from the content-type header,
|
||||
//fall back to checking the extension in the url
|
||||
return fileType(sourceUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Responsible for managing http transfers
|
||||
*/
|
||||
|
@ -68,20 +54,16 @@ export default async function downloadUrl(logger, sourceUrl, targetPath, timeout
|
|||
|
||||
try {
|
||||
let totalSize = parseFloat(resp.headers['content-length']) || 0;
|
||||
const progressReporter = getProgressReporter(logger);
|
||||
progressReporter.init(totalSize);
|
||||
const progress = new Progress(logger);
|
||||
progress.init(totalSize);
|
||||
|
||||
await downloadResponse({ resp, targetPath, progressReporter });
|
||||
await downloadResponse({ resp, targetPath, progress });
|
||||
|
||||
progressReporter.complete();
|
||||
progress.complete();
|
||||
} catch (err) {
|
||||
req.abort();
|
||||
throw err;
|
||||
}
|
||||
|
||||
// all is well, return our archive type
|
||||
const archiveType = getArchiveTypeFromResponse(resp, sourceUrl);
|
||||
return { archiveType };
|
||||
} catch (err) {
|
||||
if (err.message !== 'ENOTFOUND') {
|
||||
logger.error(err);
|
||||
|
|
|
@ -1,86 +1,40 @@
|
|||
import _ from 'lodash';
|
||||
import fromRoot from '../../utils/from_root';
|
||||
import pluginDownloader from './plugin_downloader';
|
||||
import pluginCleaner from './plugin_cleaner';
|
||||
import pluginExtractor from './plugin_extractor';
|
||||
import KbnServer from '../../server/kbn_server';
|
||||
import readYamlConfig from '../serve/read_yaml_config';
|
||||
import { download } from './download';
|
||||
import Promise from 'bluebird';
|
||||
import { cleanPrevious, cleanError } from './cleanup';
|
||||
import { extract, getPackData } from './pack';
|
||||
import { sync as rimrafSync } from 'rimraf';
|
||||
import { statSync, renameSync } from 'fs';
|
||||
import { existingInstall, rebuildCache, checkVersion } from './kibana';
|
||||
|
||||
const mkdirp = Promise.promisify(require('mkdirp'));
|
||||
|
||||
export default {
|
||||
install: install
|
||||
};
|
||||
|
||||
function checkForExistingInstall(settings, logger) {
|
||||
export default async function install(settings, logger) {
|
||||
try {
|
||||
statSync(settings.pluginPath);
|
||||
|
||||
logger.error(`Plugin ${settings.package} already exists, please remove before installing a new version`);
|
||||
process.exit(70); // eslint-disable-line no-process-exit
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function rebuildKibanaCache(settings, logger) {
|
||||
logger.log('Optimizing and caching browser bundles...');
|
||||
const serverConfig = _.merge(
|
||||
readYamlConfig(settings.config),
|
||||
{
|
||||
env: 'production',
|
||||
logging: {
|
||||
silent: settings.silent,
|
||||
quiet: !settings.silent,
|
||||
verbose: false
|
||||
},
|
||||
optimize: {
|
||||
useBundleCache: false
|
||||
},
|
||||
server: {
|
||||
autoListen: false
|
||||
},
|
||||
plugins: {
|
||||
initialize: false,
|
||||
scanDirs: [settings.pluginDir, fromRoot('src/plugins')]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const kbnServer = new KbnServer(serverConfig);
|
||||
await kbnServer.ready();
|
||||
await kbnServer.close();
|
||||
}
|
||||
|
||||
async function install(settings, logger) {
|
||||
logger.log(`Installing ${settings.package}`);
|
||||
|
||||
const cleaner = pluginCleaner(settings, logger);
|
||||
|
||||
try {
|
||||
checkForExistingInstall(settings, logger);
|
||||
|
||||
await cleaner.cleanPrevious();
|
||||
await cleanPrevious(settings, logger);
|
||||
|
||||
await mkdirp(settings.workingPath);
|
||||
|
||||
const downloader = pluginDownloader(settings, logger);
|
||||
const { archiveType } = await downloader.download();
|
||||
await download(settings, logger);
|
||||
|
||||
await pluginExtractor (settings, logger, archiveType);
|
||||
await getPackData(settings, logger);
|
||||
|
||||
await extract (settings, logger);
|
||||
|
||||
rimrafSync(settings.tempArchiveFile);
|
||||
|
||||
renameSync(settings.workingPath, settings.pluginPath);
|
||||
existingInstall(settings, logger);
|
||||
|
||||
await rebuildKibanaCache(settings, logger);
|
||||
checkVersion(settings);
|
||||
|
||||
renameSync(settings.workingPath, settings.plugins[0].path);
|
||||
|
||||
await rebuildCache(settings, logger);
|
||||
|
||||
logger.log('Plugin installation complete');
|
||||
} catch (err) {
|
||||
logger.error(`Plugin installation was unsuccessful due to error "${err.message}"`);
|
||||
cleaner.cleanError();
|
||||
cleanError(settings);
|
||||
process.exit(70); // eslint-disable-line no-process-exit
|
||||
}
|
||||
}
|
||||
|
|
56
src/cli_plugin/install/kibana.js
Normal file
56
src/cli_plugin/install/kibana.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
import _ from 'lodash';
|
||||
import fromRoot from '../../utils/fromRoot';
|
||||
import KbnServer from '../../server/KbnServer';
|
||||
import readYamlConfig from '../../cli/serve/read_yaml_config';
|
||||
import { statSync } from 'fs';
|
||||
|
||||
export function existingInstall(settings, logger) {
|
||||
try {
|
||||
statSync(settings.plugins[0].path);
|
||||
|
||||
logger.error(`Plugin ${settings.plugins[0].name} already exists, please remove before installing a new version`);
|
||||
process.exit(70); // eslint-disable-line no-process-exit
|
||||
} catch (e) {
|
||||
if (e.code !== 'ENOENT') throw e;
|
||||
}
|
||||
}
|
||||
|
||||
export async function rebuildCache(settings, logger) {
|
||||
logger.log('Optimizing and caching browser bundles...');
|
||||
const serverConfig = _.merge(
|
||||
readYamlConfig(settings.config),
|
||||
{
|
||||
env: 'production',
|
||||
logging: {
|
||||
silent: settings.silent,
|
||||
quiet: !settings.silent,
|
||||
verbose: false
|
||||
},
|
||||
optimize: {
|
||||
useBundleCache: false
|
||||
},
|
||||
server: {
|
||||
autoListen: false
|
||||
},
|
||||
plugins: {
|
||||
initialize: false,
|
||||
scanDirs: [settings.pluginDir, fromRoot('src/plugins')]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const kbnServer = new KbnServer(serverConfig);
|
||||
await kbnServer.ready();
|
||||
await kbnServer.close();
|
||||
}
|
||||
|
||||
export function checkVersion(settings) {
|
||||
if (!settings.plugins[0].version) {
|
||||
throw new Error (`Plugin version not found. Check package.json in archive`);
|
||||
}
|
||||
|
||||
if (settings.plugins[0].version !== settings.version) {
|
||||
throw new Error (`Incorrect version in plugin [${settings.plugins[0].name}]. ` +
|
||||
`Expected [${settings.version}]; found [${settings.plugins[0].version}]`);
|
||||
}
|
||||
}
|
119
src/cli_plugin/install/pack.js
Normal file
119
src/cli_plugin/install/pack.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
import _ from 'lodash';
|
||||
import { listFiles, extractFiles } from './zip';
|
||||
import { resolve } from 'path';
|
||||
import { sync as rimrafSync } from 'rimraf';
|
||||
|
||||
//*****************************************
|
||||
//Return a list of package.json files in the archive
|
||||
//*****************************************
|
||||
async function listPackages(settings) {
|
||||
const regExp = new RegExp('(kibana/([^/]+))/package.json', 'i');
|
||||
const archiveFiles = await listFiles(settings.tempArchiveFile);
|
||||
|
||||
let packages = archiveFiles.map((file) => {
|
||||
file = file.replace(/\\/g, '/');
|
||||
const matches = file.match(regExp);
|
||||
if (matches) {
|
||||
return {
|
||||
file: matches[0],
|
||||
folder: matches[2]
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
packages = _.chain(packages).compact().uniq().value();
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
//*****************************************
|
||||
//Extract the package.json files into the workingPath
|
||||
//*****************************************
|
||||
async function extractPackageFiles(settings, packages) {
|
||||
const filter = {
|
||||
files: packages.map((pkg) => pkg.file)
|
||||
};
|
||||
await extractFiles(settings.tempArchiveFile, settings.workingPath, 0, filter);
|
||||
}
|
||||
|
||||
//*****************************************
|
||||
//Extract the package.json files into the workingPath
|
||||
//*****************************************
|
||||
function deletePackageFiles(settings, packages) {
|
||||
packages.forEach((pkg) => {
|
||||
const fullPath = resolve(settings.workingPath, 'kibana');
|
||||
rimrafSync(fullPath);
|
||||
});
|
||||
}
|
||||
|
||||
//*****************************************
|
||||
//Examine each package.json file to determine the plugin name,
|
||||
//version, and platform.
|
||||
//*****************************************
|
||||
async function readPackageData(settings, packages) {
|
||||
return packages.map((pkg) => {
|
||||
const fullPath = resolve(settings.workingPath, pkg.file);
|
||||
const packageInfo = require(fullPath);
|
||||
|
||||
pkg.version = _.get(packageInfo, 'version');
|
||||
pkg.name = _.get(packageInfo, 'name');
|
||||
pkg.path = resolve(settings.pluginDir, pkg.name);
|
||||
|
||||
const regExp = new RegExp(`${pkg.name}-(.+)`, 'i');
|
||||
const matches = pkg.folder.match(regExp);
|
||||
pkg.platform = (matches) ? matches[1] : undefined;
|
||||
|
||||
return pkg;
|
||||
});
|
||||
}
|
||||
|
||||
//*****************************************
|
||||
//Extracts the first plugin in the archive.
|
||||
//This will need to be changed in later versions of the pack installer
|
||||
//that allow for the installation of more than one plugin at once.
|
||||
//*****************************************
|
||||
async function extractArchive(settings) {
|
||||
const filter = {
|
||||
paths: [ settings.plugins[0].folder ]
|
||||
};
|
||||
await extractFiles(settings.tempArchiveFile, settings.workingPath, 2, filter);
|
||||
}
|
||||
|
||||
//*****************************************
|
||||
//Returns the detailed information about each kibana plugin in the
|
||||
//pack.
|
||||
//TODO: If there are platform specific folders, determine which one to use.
|
||||
//*****************************************
|
||||
export async function getPackData(settings, logger) {
|
||||
let packages;
|
||||
try {
|
||||
logger.log('Retrieving metadata from plugin archive');
|
||||
|
||||
packages = await listPackages(settings);
|
||||
await extractPackageFiles(settings, packages);
|
||||
await readPackageData(settings, packages);
|
||||
await deletePackageFiles(settings, packages);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
throw new Error('Error retrieving metadata from plugin archive');
|
||||
}
|
||||
|
||||
if (packages.length === 0) {
|
||||
throw new Error('No kibana plugins found in archive');
|
||||
}
|
||||
|
||||
settings.plugins = packages;
|
||||
}
|
||||
|
||||
export async function extract(settings, logger) {
|
||||
try {
|
||||
logger.log('Extracting plugin archive');
|
||||
|
||||
await extractArchive(settings);
|
||||
|
||||
logger.log('Extraction complete');
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
throw new Error('Error extracting plugin archive');
|
||||
}
|
||||
};
|
|
@ -1,38 +1,39 @@
|
|||
/*
|
||||
Generates file transfer progress messages
|
||||
*/
|
||||
export default function createProgressReporter(logger) {
|
||||
let dotCount = 0;
|
||||
let runningTotal = 0;
|
||||
let totalSize = 0;
|
||||
export default function Progress(logger) {
|
||||
const self = this;
|
||||
|
||||
function init(size) {
|
||||
totalSize = size;
|
||||
let totalDesc = totalSize || 'unknown number of';
|
||||
self.dotCount = 0;
|
||||
self.runningTotal = 0;
|
||||
self.totalSize = 0;
|
||||
self.logger = logger;
|
||||
}
|
||||
|
||||
logger.log(`Transferring ${totalDesc} bytes`, true);
|
||||
}
|
||||
Progress.prototype.init = function (size) {
|
||||
const self = this;
|
||||
|
||||
//Should log a dot for every 5% of progress
|
||||
function progress(size) {
|
||||
if (!totalSize) return;
|
||||
self.totalSize = size;
|
||||
const totalDesc = self.totalSize || 'unknown number of';
|
||||
|
||||
runningTotal += size;
|
||||
let newDotCount = Math.round(runningTotal / totalSize * 100 / 5);
|
||||
if (newDotCount > 20) newDotCount = 20;
|
||||
for (let i = 0; i < (newDotCount - dotCount); i++) {
|
||||
logger.log('.', true);
|
||||
}
|
||||
dotCount = newDotCount;
|
||||
}
|
||||
|
||||
function complete() {
|
||||
logger.log(`Transfer complete`, false);
|
||||
}
|
||||
|
||||
return {
|
||||
init: init,
|
||||
progress: progress,
|
||||
complete: complete
|
||||
};
|
||||
self.logger.log(`Transferring ${totalDesc} bytes`, true);
|
||||
};
|
||||
|
||||
Progress.prototype.progress = function (size) {
|
||||
const self = this;
|
||||
|
||||
if (!self.totalSize) return;
|
||||
|
||||
self.runningTotal += size;
|
||||
let newDotCount = Math.round(self.runningTotal / self.totalSize * 100 / 5);
|
||||
if (newDotCount > 20) newDotCount = 20;
|
||||
for (let i = 0; i < (newDotCount - self.dotCount); i++) {
|
||||
self.logger.log('.', true);
|
||||
}
|
||||
self.dotCount = newDotCount;
|
||||
};
|
||||
|
||||
Progress.prototype.complete = function () {
|
||||
const self = this;
|
||||
self.logger.log(`Transfer complete`, false);
|
||||
};
|
||||
|
|
|
@ -1,114 +1,48 @@
|
|||
import expiry from 'expiry-js';
|
||||
import { intersection } from 'lodash';
|
||||
import { resolve } from 'path';
|
||||
import { arch, platform } from 'os';
|
||||
|
||||
export default function createSettingParser(options) {
|
||||
function parseMilliseconds(val) {
|
||||
let result;
|
||||
function generateUrls(settings) {
|
||||
const { version, plugin } = settings;
|
||||
return [
|
||||
plugin,
|
||||
`https://download.elastic.co/packs/${plugin}/${plugin}-${version}.zip`
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
let timeVal = expiry(val);
|
||||
result = timeVal.asMilliseconds();
|
||||
} catch (ex) {
|
||||
result = 0;
|
||||
}
|
||||
export function parseMilliseconds(val) {
|
||||
let result;
|
||||
|
||||
return result;
|
||||
try {
|
||||
let timeVal = expiry(val);
|
||||
result = timeVal.asMilliseconds();
|
||||
} catch (ex) {
|
||||
result = 0;
|
||||
}
|
||||
|
||||
function generateDownloadUrl(settings) {
|
||||
const version = (settings.version) || 'latest';
|
||||
const filename = settings.package + '-' + version + '.tar.gz';
|
||||
|
||||
return 'https://download.elastic.co/' + settings.organization + '/' + settings.package + '/' + filename;
|
||||
}
|
||||
|
||||
function areMultipleOptionsChosen(options, choices) {
|
||||
return intersection(Object.keys(options), choices).length > 1;
|
||||
}
|
||||
|
||||
function parse() {
|
||||
let parts;
|
||||
let settings = {
|
||||
timeout: 0,
|
||||
silent: false,
|
||||
quiet: false,
|
||||
urls: []
|
||||
};
|
||||
|
||||
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.url) {
|
||||
settings.urls.push(options.url);
|
||||
}
|
||||
|
||||
if (options.config) {
|
||||
settings.config = options.config;
|
||||
}
|
||||
|
||||
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 <plugin>.');
|
||||
}
|
||||
|
||||
settings.package = parts.shift();
|
||||
} else {
|
||||
if (parts.length < 2 || parts.length > 3) {
|
||||
throw new Error('Invalid install option. Please use the format <org>/<plugin>/<version>.');
|
||||
}
|
||||
|
||||
settings.organization = parts.shift();
|
||||
settings.package = parts.shift();
|
||||
settings.version = parts.shift();
|
||||
|
||||
settings.urls.push(generateDownloadUrl(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 <plugin>.');
|
||||
}
|
||||
settings.package = parts.shift();
|
||||
}
|
||||
|
||||
if (options.list) {
|
||||
settings.action = 'list';
|
||||
}
|
||||
|
||||
if (!settings.action || areMultipleOptionsChosen(options, [ 'install', 'remove', 'list' ])) {
|
||||
throw new Error('Please specify either --install, --remove, or --list.');
|
||||
}
|
||||
|
||||
settings.pluginDir = options.pluginDir;
|
||||
if (settings.package) {
|
||||
settings.pluginPath = resolve(settings.pluginDir, settings.package);
|
||||
settings.workingPath = resolve(settings.pluginDir, '.plugin.installing');
|
||||
settings.tempArchiveFile = resolve(settings.workingPath, 'archive.part');
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
return {
|
||||
parse: parse,
|
||||
parseMilliseconds: parseMilliseconds
|
||||
};
|
||||
return result;
|
||||
};
|
||||
|
||||
export function parse(command, options, kbnPackage) {
|
||||
const settings = {
|
||||
timeout: options.timeout ? options.timeout : 0,
|
||||
quiet: options.quiet ? options.quiet : false,
|
||||
silent: options.silent ? options.silent : false,
|
||||
config: options.config ? options.config : '',
|
||||
plugin: command,
|
||||
version: kbnPackage.version,
|
||||
pluginDir: options.pluginDir ? options.pluginDir : ''
|
||||
};
|
||||
|
||||
settings.urls = generateUrls(settings);
|
||||
settings.workingPath = resolve(settings.pluginDir, '.plugin.installing');
|
||||
settings.tempArchiveFile = resolve(settings.workingPath, 'archive.part');
|
||||
settings.tempPackageFile = resolve(settings.workingPath, 'package.json');
|
||||
settings.setPlugin = function (plugin) {
|
||||
settings.plugin = plugin;
|
||||
settings.pluginPath = resolve(settings.pluginDir, settings.plugin.name);
|
||||
};
|
||||
|
||||
return settings;
|
||||
};
|
||||
|
|
|
@ -1,32 +1,71 @@
|
|||
import _ from 'lodash';
|
||||
import DecompressZip from '@bigfunger/decompress-zip';
|
||||
|
||||
async function extractArchive(settings) {
|
||||
//***********************************************
|
||||
//Creates a filter function to be consumed by extractFiles
|
||||
//
|
||||
//filter: an object with either a files or paths property.
|
||||
//filter.files: an array of full file paths to extract. Should match
|
||||
// exactly a value from listFiles
|
||||
//filter.paths: an array of root paths from the archive. All files and
|
||||
// folders will be extracted recursively using these paths as roots.
|
||||
//***********************************************
|
||||
function extractFilter(filter) {
|
||||
if (filter.files) {
|
||||
const filterFiles = filter.files.map((file) => file.replace(/\\/g, '/'));
|
||||
return function filterByFiles(file) {
|
||||
if (file.type === 'SymbolicLink') return false;
|
||||
|
||||
const path = file.path.replace(/\\/g, '/');
|
||||
return !!(_.indexOf(filterFiles, path) !== -1);
|
||||
};
|
||||
}
|
||||
|
||||
if (filter.paths) {
|
||||
return function filterByRootPath(file) {
|
||||
if (file.type === 'SymbolicLink') return false;
|
||||
|
||||
let include = false;
|
||||
filter.paths.forEach((path) => {
|
||||
const regex = new RegExp(`${path}($|/)`, 'i');
|
||||
if ((file.parent.match(regex)) && file.type !== 'SymbolicLink') {
|
||||
include = true;
|
||||
}
|
||||
});
|
||||
|
||||
return include;
|
||||
};
|
||||
}
|
||||
|
||||
return _.noop;
|
||||
}
|
||||
|
||||
export async function extractFiles(zipPath, targetPath, strip, filter) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const unzipper = new DecompressZip(settings.tempArchiveFile);
|
||||
const unzipper = new DecompressZip(zipPath);
|
||||
|
||||
unzipper.on('error', reject);
|
||||
|
||||
unzipper.extract({
|
||||
path: settings.workingPath,
|
||||
strip: 1,
|
||||
filter(file) {
|
||||
return file.type !== 'SymbolicLink';
|
||||
}
|
||||
path: targetPath,
|
||||
strip: strip,
|
||||
filter: extractFilter(filter)
|
||||
});
|
||||
|
||||
unzipper.on('extract', resolve);
|
||||
});
|
||||
}
|
||||
|
||||
export default async function extractZip(settings, logger) {
|
||||
try {
|
||||
logger.log('Extracting plugin archive');
|
||||
export async function listFiles(zipPath) {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const unzipper = new DecompressZip(zipPath);
|
||||
|
||||
await extractArchive(settings);
|
||||
unzipper.on('error', reject);
|
||||
|
||||
logger.log('Extraction complete');
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
throw new Error('Error extracting plugin archive');
|
||||
}
|
||||
};
|
||||
unzipper.on('list', (files) => {
|
||||
resolve(files);
|
||||
});
|
||||
|
||||
unzipper.list();
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue