Merge branch '0.15'

This commit is contained in:
Girish Ramakrishnan
2016-06-08 11:56:24 -07:00
6 changed files with 61 additions and 11 deletions
+1
View File
@@ -525,4 +525,5 @@
- Enable Developer mode by default for new Cloudrons - Enable Developer mode by default for new Cloudrons
- Reverse proxy fixes for apps exposing a WebDav server - Reverse proxy fixes for apps exposing a WebDav server
- Allow admins to optionally set the username and displayName on user creation - Allow admins to optionally set the username and displayName on user creation
- Fix app autoupdate logic to detect if one or more in-use port bindings was removed
+11 -8
View File
@@ -350,6 +350,8 @@ function purchase(appStoreId, callback) {
} }
function downloadManifest(appStoreId, manifest, callback) { function downloadManifest(appStoreId, manifest, callback) {
if (!appStoreId && !manifest) return callback(new AppsError(AppsError.BAD_FIELD, 'Neither manifest nor appStoreId provided'));
if (!appStoreId) return callback(null, '', manifest); if (!appStoreId) return callback(null, '', manifest);
var parts = appStoreId.split('@'); var parts = appStoreId.split('@');
@@ -841,18 +843,19 @@ function updateApps(updateInfo, auditSource, callback) { // updateInfo is { appI
assert.strictEqual(typeof callback, 'function'); assert.strictEqual(typeof callback, 'function');
function canAutoupdateApp(app, newManifest) { function canAutoupdateApp(app, newManifest) {
var tcpPorts = newManifest.tcpPorts || { }; var newTcpPorts = newManifest.newTcpPorts || { };
var oldTcpPorts = app.manifest.tcpPorts || { };
var portBindings = app.portBindings; // this is never null var portBindings = app.portBindings; // this is never null
if (Object.keys(tcpPorts).length === 0 && Object.keys(portBindings).length === 0) return null; for (var env in newTcpPorts) {
if (Object.keys(tcpPorts).length === 0) return new Error('tcpPorts is now empty but portBindings is not'); if (!(env in oldTcpPorts)) return new Error(env + ' is required from user');
if (Object.keys(portBindings).length === 0) return new Error('portBindings is now empty but tcpPorts is not');
for (var env in tcpPorts) {
if (!(env in portBindings)) return new Error(env + ' is required from user');
} }
// it's fine if one or more keys got removed for (env in portBindings) {
if (!(env in newTcpPorts)) return new Error(env + ' was in use but new update removes it');
}
// it's fine if one or more (unused) keys got removed
return null; return null;
} }
+1 -1
View File
@@ -114,7 +114,7 @@ function getTxtRecords(callback) {
function checkDns() { function checkDns() {
getTxtRecords(function (error, records) { getTxtRecords(function (error, records) {
if (error || !records) { if (error || !records) {
debug('checkDns: DNS error or no records looking up TXT records for %s %s', config.adminFqdn(), error, records); debug('checkDns: DNS error or no records looking up TXT records for %s %s', config.fqdn(), error, records);
gCheckDnsTimerId = setTimeout(checkDns, 60000); gCheckDnsTimerId = setTimeout(checkDns, 60000);
return; return;
} }
+14 -2
View File
@@ -8,10 +8,12 @@ exports = module.exports = {
getProgress: getProgress, getProgress: getProgress,
getConfig: getConfig, getConfig: getConfig,
update: update, update: update,
feedback: feedback feedback: feedback,
checkForUpdates: checkForUpdates
}; };
var assert = require('assert'), var assert = require('assert'),
async = require('async'),
cloudron = require('../cloudron.js'), cloudron = require('../cloudron.js'),
CloudronError = cloudron.CloudronError, CloudronError = cloudron.CloudronError,
config = require('../config.js'), config = require('../config.js'),
@@ -20,7 +22,8 @@ var assert = require('assert'),
HttpSuccess = require('connect-lastmile').HttpSuccess, HttpSuccess = require('connect-lastmile').HttpSuccess,
progress = require('../progress.js'), progress = require('../progress.js'),
mailer = require('../mailer.js'), mailer = require('../mailer.js'),
superagent = require('superagent'); superagent = require('superagent'),
updateChecker = require('../updatechecker.js');
function auditSource(req) { function auditSource(req) {
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || null; var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || null;
@@ -129,6 +132,15 @@ function update(req, res, next) {
}); });
} }
function checkForUpdates(req, res, next) {
async.series([
updateChecker.checkAppUpdates,
updateChecker.checkBoxUpdates
], function () {
next(new HttpSuccess(200, { update: updateChecker.getUpdateInfo() }));
});
}
function feedback(req, res, next) { function feedback(req, res, next) {
assert.strictEqual(typeof req.user, 'object'); assert.strictEqual(typeof req.user, 'object');
+33
View File
@@ -229,6 +229,39 @@ describe('Apps', function () {
}); });
}); });
it('app install fails - null manifest', function (done) {
superagent.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token })
.send({ manifest: null, password: PASSWORD })
.end(function (err, res) {
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.eql('appStoreId or manifest is required');
done();
});
});
it('app install fails - bad manifest format', function (done) {
superagent.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token })
.send({ manifest: 'epic', password: PASSWORD })
.end(function (err, res) {
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.eql('manifest must be an object');
done();
});
});
it('app install fails - empty appStoreId format', function (done) {
superagent.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token })
.send({ manifest: null, appStoreId: '', password: PASSWORD })
.end(function (err, res) {
expect(res.statusCode).to.equal(400);
expect(res.body.message).to.eql('appStoreId or manifest is required');
done();
});
});
it('app install fails - invalid json', function (done) { it('app install fails - invalid json', function (done) {
superagent.post(SERVER_URL + '/api/v1/apps/install') superagent.post(SERVER_URL + '/api/v1/apps/install')
.query({ access_token: token }) .query({ access_token: token })
+1
View File
@@ -94,6 +94,7 @@ function initializeExpressSync() {
// cloudron routes // cloudron routes
router.get ('/api/v1/cloudron/config', cloudronScope, routes.cloudron.getConfig); router.get ('/api/v1/cloudron/config', cloudronScope, routes.cloudron.getConfig);
router.post('/api/v1/cloudron/update', cloudronScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.cloudron.update); router.post('/api/v1/cloudron/update', cloudronScope, routes.user.requireAdmin, routes.user.verifyPassword, routes.cloudron.update);
router.post('/api/v1/cloudron/check_for_updates', cloudronScope, routes.user.requireAdmin, routes.cloudron.checkForUpdates);
router.post('/api/v1/cloudron/reboot', cloudronScope, routes.cloudron.reboot); router.post('/api/v1/cloudron/reboot', cloudronScope, routes.cloudron.reboot);
router.get ('/api/v1/cloudron/graphs', cloudronScope, routes.graphs.getGraphs); router.get ('/api/v1/cloudron/graphs', cloudronScope, routes.graphs.getGraphs);