statically allocate app container IPs
We removed httpPort with the assumption that docker allocated IPs and kept them as long as the container is around. This turned out to be not true because the IP changes on even container restart. So we now allocate IPs statically. The iprange makes sure we don't overlap with addons and other CI app or JupyterHub apps. https://github.com/moby/moby/issues/6743 https://github.com/moby/moby/pull/19001
This commit is contained in:
+40
-6
@@ -33,6 +33,7 @@ var addons = require('./addons.js'),
|
||||
ejs = require('ejs'),
|
||||
eventlog = require('./eventlog.js'),
|
||||
fs = require('fs'),
|
||||
iputils = require('./iputils.js'),
|
||||
manifestFormat = require('cloudron-manifestformat'),
|
||||
os = require('os'),
|
||||
path = require('path'),
|
||||
@@ -87,6 +88,18 @@ function updateApp(app, values, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function allocateContainerIp(app, callback) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
|
||||
async.retry({ times: 10 }, function (retryCallback) {
|
||||
const iprange = iputils.intFromIp('172.18.20.255') - iputils.intFromIp('172.18.16.1');
|
||||
let rnd = Math.floor(Math.random() * iprange);
|
||||
const containerIp = iputils.ipFromInt(iputils.intFromIp('172.18.16.1') + rnd);
|
||||
updateApp(app, { containerIp }, retryCallback);
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function configureReverseProxy(app, callback) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert.strictEqual(typeof callback, 'function');
|
||||
@@ -144,7 +157,6 @@ function deleteContainers(app, options, callback) {
|
||||
removeLogrotateConfig.bind(null, app),
|
||||
docker.stopContainers.bind(null, app.id),
|
||||
docker.deleteContainers.bind(null, app.id, options),
|
||||
unconfigureReverseProxy.bind(null, app),
|
||||
updateApp.bind(null, app, { containerId: null })
|
||||
], callback);
|
||||
}
|
||||
@@ -469,10 +481,7 @@ function startApp(app, callback){
|
||||
|
||||
if (app.runState === apps.RSTATE_STOPPED) return callback();
|
||||
|
||||
async.series([
|
||||
docker.startContainer.bind(null, app.id),
|
||||
configureReverseProxy.bind(null, app),
|
||||
], callback);
|
||||
docker.startContainer(app.id, callback);
|
||||
}
|
||||
|
||||
function install(app, args, progressCallback, callback) {
|
||||
@@ -492,6 +501,7 @@ function install(app, args, progressCallback, callback) {
|
||||
|
||||
// teardown for re-installs
|
||||
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
|
||||
unconfigureReverseProxy.bind(null, app),
|
||||
deleteContainers.bind(null, app, { managedOnly: true }),
|
||||
function teardownAddons(next) {
|
||||
// when restoring, app does not require these addons anymore. remove carefully to preserve the db passwords
|
||||
@@ -517,6 +527,9 @@ function install(app, args, progressCallback, callback) {
|
||||
docker.deleteImage(oldManifest, done);
|
||||
},
|
||||
|
||||
// allocating container ip here, lets the users "repair" an app if allocation fails at appdb.add time
|
||||
allocateContainerIp.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 20, message: 'Downloading icon' }),
|
||||
downloadIcon.bind(null, app),
|
||||
|
||||
@@ -563,6 +576,9 @@ function install(app, args, progressCallback, callback) {
|
||||
progressCallback.bind(null, { percent: 85, message: 'Waiting for DNS propagation' }),
|
||||
exports._waitForDnsPropagation.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 95, message: 'Configuring reverse proxy' }),
|
||||
configureReverseProxy.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 100, message: 'Done' }),
|
||||
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
|
||||
], function seriesDone(error) {
|
||||
@@ -640,6 +656,7 @@ function changeLocation(app, args, progressCallback, callback) {
|
||||
|
||||
async.series([
|
||||
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
|
||||
unconfigureReverseProxy.bind(null, app),
|
||||
deleteContainers.bind(null, app, { managedOnly: true }),
|
||||
function (next) {
|
||||
let obsoleteDomains = oldConfig.alternateDomains.filter(function (o) {
|
||||
@@ -668,6 +685,9 @@ function changeLocation(app, args, progressCallback, callback) {
|
||||
progressCallback.bind(null, { percent: 80, message: 'Waiting for DNS propagation' }),
|
||||
exports._waitForDnsPropagation.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 90, message: 'Configuring reverse proxy' }),
|
||||
configureReverseProxy.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 100, message: 'Done' }),
|
||||
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
|
||||
], function seriesDone(error) {
|
||||
@@ -728,6 +748,7 @@ function configure(app, args, progressCallback, callback) {
|
||||
|
||||
async.series([
|
||||
progressCallback.bind(null, { percent: 10, message: 'Cleaning up old install' }),
|
||||
unconfigureReverseProxy.bind(null, app),
|
||||
deleteContainers.bind(null, app, { managedOnly: true }),
|
||||
|
||||
progressCallback.bind(null, { percent: 20, message: 'Downloading icon' }),
|
||||
@@ -748,6 +769,9 @@ function configure(app, args, progressCallback, callback) {
|
||||
|
||||
startApp.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 90, message: 'Configuring reverse proxy' }),
|
||||
configureReverseProxy.bind(null, app),
|
||||
|
||||
progressCallback.bind(null, { percent: 100, message: 'Done' }),
|
||||
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null })
|
||||
], function seriesDone(error) {
|
||||
@@ -771,7 +795,8 @@ function update(app, args, progressCallback, callback) {
|
||||
|
||||
// app does not want these addons anymore
|
||||
// FIXME: this does not handle option changes (like multipleDatabases)
|
||||
var unusedAddons = _.omit(app.manifest.addons, Object.keys(updateConfig.manifest.addons));
|
||||
const unusedAddons = _.omit(app.manifest.addons, Object.keys(updateConfig.manifest.addons));
|
||||
const httpPathsChanged = app.manifest.httpPaths !== updateConfig.manifest.httpPaths;
|
||||
|
||||
async.series([
|
||||
// this protects against the theoretical possibility of an app being marked for update from
|
||||
@@ -846,6 +871,14 @@ function update(app, args, progressCallback, callback) {
|
||||
|
||||
startApp.bind(null, app),
|
||||
|
||||
// needed for httpPaths changes
|
||||
progressCallback.bind(null, { percent: 90, message: 'Configuring reverse proxy' }),
|
||||
function (next) {
|
||||
if (!httpPathsChanged) return next();
|
||||
|
||||
configureReverseProxy(app, next);
|
||||
},
|
||||
|
||||
progressCallback.bind(null, { percent: 100, message: 'Done' }),
|
||||
updateApp.bind(null, app, { installationState: apps.ISTATE_INSTALLED, error: null, health: null, updateTime: new Date() })
|
||||
], function seriesDone(error) {
|
||||
@@ -948,6 +981,7 @@ function uninstall(app, args, progressCallback, callback) {
|
||||
|
||||
async.series([
|
||||
progressCallback.bind(null, { percent: 20, message: 'Deleting container' }),
|
||||
unconfigureReverseProxy.bind(null, app),
|
||||
deleteContainers.bind(null, app, {}),
|
||||
|
||||
progressCallback.bind(null, { percent: 30, message: 'Teardown addons' }),
|
||||
|
||||
Reference in New Issue
Block a user