2015-07-20 00:09:47 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
2015-10-19 11:40:19 -07:00
|
|
|
var addons = require('./addons.js'),
|
|
|
|
|
async = require('async'),
|
|
|
|
|
assert = require('assert'),
|
|
|
|
|
config = require('./config.js'),
|
2016-02-14 12:13:49 +01:00
|
|
|
constants = require('./constants.js'),
|
2015-10-19 23:38:55 -07:00
|
|
|
debug = require('debug')('box:src/docker.js'),
|
2015-10-19 11:40:19 -07:00
|
|
|
Docker = require('dockerode'),
|
|
|
|
|
safe = require('safetydance'),
|
|
|
|
|
semver = require('semver'),
|
2015-11-10 12:47:48 -08:00
|
|
|
util = require('util'),
|
|
|
|
|
_ = require('underscore');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-10-19 11:24:21 -07:00
|
|
|
exports = module.exports = {
|
2015-10-19 11:40:19 -07:00
|
|
|
connection: connectionInstance(),
|
|
|
|
|
downloadImage: downloadImage,
|
|
|
|
|
createContainer: createContainer,
|
|
|
|
|
startContainer: startContainer,
|
|
|
|
|
stopContainer: stopContainer,
|
2015-12-23 13:23:47 -08:00
|
|
|
stopContainerByName: stopContainer,
|
2015-10-20 00:05:07 -07:00
|
|
|
stopContainers: stopContainers,
|
2015-10-19 11:40:19 -07:00
|
|
|
deleteContainer: deleteContainer,
|
2015-12-23 13:23:47 -08:00
|
|
|
deleteContainerByName: deleteContainer,
|
2015-10-19 18:48:56 -07:00
|
|
|
deleteImage: deleteImage,
|
2015-10-20 09:36:30 -07:00
|
|
|
deleteContainers: deleteContainers,
|
2016-02-18 15:39:27 +01:00
|
|
|
createSubcontainer: createSubcontainer,
|
|
|
|
|
getContainerIdByIp: getContainerIdByIp
|
2015-10-19 11:24:21 -07:00
|
|
|
};
|
2015-10-19 11:08:23 -07:00
|
|
|
|
|
|
|
|
function connectionInstance() {
|
2015-07-20 00:09:47 -07:00
|
|
|
var docker;
|
|
|
|
|
|
2015-07-24 01:42:28 -07:00
|
|
|
if (process.env.BOX_ENV === 'test') {
|
2015-07-20 00:09:47 -07:00
|
|
|
// test code runs a docker proxy on this port
|
|
|
|
|
docker = new Docker({ host: 'http://localhost', port: 5687 });
|
2015-10-19 11:08:23 -07:00
|
|
|
|
|
|
|
|
// proxy code uses this to route to the real docker
|
|
|
|
|
docker.options = { socketPath: '/var/run/docker.sock' };
|
2015-07-20 00:09:47 -07:00
|
|
|
} else {
|
2015-10-19 11:08:23 -07:00
|
|
|
docker = new Docker({ socketPath: '/var/run/docker.sock' });
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return docker;
|
|
|
|
|
}
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
function debugApp(app, args) {
|
|
|
|
|
assert(!app || typeof app === 'object');
|
|
|
|
|
|
|
|
|
|
var prefix = app ? (app.location || '(bare)') : '(no app)';
|
|
|
|
|
debug(prefix + ' ' + util.format.apply(util, Array.prototype.slice.call(arguments, 1)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function targetBoxVersion(manifest) {
|
|
|
|
|
if ('targetBoxVersion' in manifest) return manifest.targetBoxVersion;
|
|
|
|
|
|
|
|
|
|
if ('minBoxVersion' in manifest) return manifest.minBoxVersion;
|
|
|
|
|
|
2015-12-27 16:43:45 -08:00
|
|
|
return '99999.99999.99999'; // compatible with the latest version
|
2015-10-19 11:40:19 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:37:57 -07:00
|
|
|
function pullImage(manifest, callback) {
|
2015-10-19 11:40:19 -07:00
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
2015-10-19 15:37:57 -07:00
|
|
|
docker.pull(manifest.dockerImage, function (err, stream) {
|
2015-10-19 11:40:19 -07:00
|
|
|
if (err) return callback(new Error('Error connecting to docker. statusCode: %s' + err.statusCode));
|
|
|
|
|
|
|
|
|
|
// https://github.com/dotcloud/docker/issues/1074 says each status message
|
|
|
|
|
// is emitted as a chunk
|
|
|
|
|
stream.on('data', function (chunk) {
|
|
|
|
|
var data = safe.JSON.parse(chunk) || { };
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('pullImage %s: %j', manifest.id, data);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
// The information here is useless because this is per layer as opposed to per image
|
|
|
|
|
if (data.status) {
|
|
|
|
|
} else if (data.error) {
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('pullImage error %s: %s', manifest.id, data.errorDetail.message);
|
2015-10-19 11:40:19 -07:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
stream.on('end', function () {
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('downloaded image %s of %s successfully', manifest.dockerImage, manifest.id);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2015-10-19 15:37:57 -07:00
|
|
|
var image = docker.getImage(manifest.dockerImage);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
image.inspect(function (err, data) {
|
|
|
|
|
if (err) return callback(new Error('Error inspecting image:' + err.message));
|
|
|
|
|
if (!data || !data.Config) return callback(new Error('Missing Config in image:' + JSON.stringify(data, null, 4)));
|
|
|
|
|
if (!data.Config.Entrypoint && !data.Config.Cmd) return callback(new Error('Only images with entry point are allowed'));
|
|
|
|
|
|
2015-11-12 16:13:15 -08:00
|
|
|
debug('This image of %s exposes ports: %j', manifest.id, data.Config.ExposedPorts);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
callback(null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
stream.on('error', function (error) {
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('error pulling image %s of %s: %j', manifest.dockerImage, manifest.id, error);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
callback(error);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:37:57 -07:00
|
|
|
function downloadImage(manifest, callback) {
|
2015-10-19 15:51:02 -07:00
|
|
|
assert.strictEqual(typeof manifest, 'object');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('downloadImage %s %s', manifest.id, manifest.dockerImage);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
var attempt = 1;
|
|
|
|
|
|
2015-11-12 16:22:53 -08:00
|
|
|
async.retry({ times: 10, interval: 15000 }, function (retryCallback) {
|
2015-11-12 15:58:39 -08:00
|
|
|
debug('Downloading image %s %s. attempt: %s', manifest.id, manifest.dockerImage, attempt++);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2015-10-19 15:37:57 -07:00
|
|
|
pullImage(manifest, function (error) {
|
2015-10-19 11:40:19 -07:00
|
|
|
if (error) console.error(error);
|
|
|
|
|
|
|
|
|
|
retryCallback(error);
|
|
|
|
|
});
|
|
|
|
|
}, callback);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-10 12:47:48 -08:00
|
|
|
function createSubcontainer(app, name, cmd, options, callback) {
|
2015-10-19 11:40:19 -07:00
|
|
|
assert.strictEqual(typeof app, 'object');
|
2015-11-02 09:34:31 -08:00
|
|
|
assert.strictEqual(typeof name, 'string');
|
2015-10-19 16:22:35 -07:00
|
|
|
assert(!cmd || util.isArray(cmd));
|
2015-11-10 12:47:48 -08:00
|
|
|
assert.strictEqual(typeof options, 'object');
|
2015-10-19 11:40:19 -07:00
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-10-20 10:51:43 -07:00
|
|
|
var docker = exports.connection,
|
|
|
|
|
isAppContainer = !cmd;
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
var manifest = app.manifest;
|
2016-01-15 11:28:14 -08:00
|
|
|
var developmentMode = !!manifest.developmentMode;
|
2015-10-19 11:40:19 -07:00
|
|
|
var exposedPorts = {}, dockerPortBindings = { };
|
|
|
|
|
var stdEnv = [
|
|
|
|
|
'CLOUDRON=1',
|
2015-10-23 16:10:54 -07:00
|
|
|
'WEBADMIN_ORIGIN=' + config.adminOrigin(),
|
|
|
|
|
'API_ORIGIN=' + config.adminOrigin(),
|
|
|
|
|
'APP_ORIGIN=https://' + config.appFqdn(app.location),
|
|
|
|
|
'APP_DOMAIN=' + config.appFqdn(app.location)
|
2015-10-19 11:40:19 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// docker portBindings requires ports to be exposed
|
|
|
|
|
exposedPorts[manifest.httpPort + '/tcp'] = {};
|
|
|
|
|
|
|
|
|
|
dockerPortBindings[manifest.httpPort + '/tcp'] = [ { HostIp: '127.0.0.1', HostPort: app.httpPort + '' } ];
|
|
|
|
|
|
2015-10-19 16:00:40 -07:00
|
|
|
var portEnv = [];
|
2015-10-19 11:40:19 -07:00
|
|
|
for (var e in app.portBindings) {
|
|
|
|
|
var hostPort = app.portBindings[e];
|
|
|
|
|
var containerPort = manifest.tcpPorts[e].containerPort || hostPort;
|
|
|
|
|
|
|
|
|
|
exposedPorts[containerPort + '/tcp'] = {};
|
2015-10-19 16:00:40 -07:00
|
|
|
portEnv.push(e + '=' + hostPort);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
dockerPortBindings[containerPort + '/tcp'] = [ { HostIp: '0.0.0.0', HostPort: hostPort + '' } ];
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-11 18:13:42 +01:00
|
|
|
// first check db record, then manifest
|
|
|
|
|
var memoryLimit = app.memoryLimit || manifest.memoryLimit;
|
|
|
|
|
|
|
|
|
|
// ensure we never go below minimum
|
2016-02-14 12:13:49 +01:00
|
|
|
memoryLimit = memoryLimit < constants.DEFAULT_MEMORY_LIMIT ? constants.DEFAULT_MEMORY_LIMIT : memoryLimit; // 256mb by default
|
2016-02-11 18:13:42 +01:00
|
|
|
|
|
|
|
|
// developerMode does not restrict memory usage
|
|
|
|
|
memoryLimit = developmentMode ? 0 : memoryLimit;
|
2016-02-11 17:00:21 +01:00
|
|
|
|
2015-12-01 13:59:45 -08:00
|
|
|
// for subcontainers, this should ideally be false. but docker does not allow network sharing if the app container is not running
|
|
|
|
|
// this means cloudron exec does not work
|
2016-01-15 15:15:24 -08:00
|
|
|
var isolatedNetworkNs = true;
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2015-10-19 16:00:40 -07:00
|
|
|
addons.getEnvironment(app, function (error, addonEnv) {
|
|
|
|
|
if (error) return callback(new Error('Error getting addon environment : ' + error));
|
|
|
|
|
|
|
|
|
|
var containerOptions = {
|
2015-11-02 10:31:40 -08:00
|
|
|
name: name, // used for filtering logs
|
2015-10-23 16:10:54 -07:00
|
|
|
// do _not_ set hostname to app fqdn. doing so sets up the dns name to look up the internal docker ip. this makes curl from within container fail
|
2015-11-26 19:26:25 -08:00
|
|
|
// for subcontainers, this should not be set because we already share the network namespace with app container
|
2015-12-05 02:27:54 -08:00
|
|
|
Hostname: isolatedNetworkNs ? (semver.gte(targetBoxVersion(app.manifest), '0.0.77') ? app.location : config.appFqdn(app.location)) : null,
|
2015-10-20 10:51:43 -07:00
|
|
|
Tty: isAppContainer,
|
2015-10-19 16:00:40 -07:00
|
|
|
Image: app.manifest.dockerImage,
|
2016-01-19 10:20:12 -08:00
|
|
|
Cmd: (isAppContainer && developmentMode) ? [ '/bin/bash', '-c', 'echo "Development mode. Use cloudron exec to debug. Sleeping" && sleep infinity' ] : cmd,
|
2015-10-19 16:00:40 -07:00
|
|
|
Env: stdEnv.concat(addonEnv).concat(portEnv),
|
2015-10-20 10:51:43 -07:00
|
|
|
ExposedPorts: isAppContainer ? exposedPorts : { },
|
2015-10-19 16:00:40 -07:00
|
|
|
Volumes: { // see also ReadonlyRootfs
|
|
|
|
|
'/tmp': {},
|
|
|
|
|
'/run': {}
|
2015-10-19 11:40:19 -07:00
|
|
|
},
|
2015-10-19 16:01:04 -07:00
|
|
|
Labels: {
|
|
|
|
|
"location": app.location,
|
2015-10-19 21:33:53 -07:00
|
|
|
"appId": app.id,
|
2015-10-20 10:51:43 -07:00
|
|
|
"isSubcontainer": String(!isAppContainer)
|
2015-10-19 16:01:04 -07:00
|
|
|
},
|
2015-10-19 16:00:40 -07:00
|
|
|
HostConfig: {
|
|
|
|
|
Binds: addons.getBindsSync(app, app.manifest.addons),
|
|
|
|
|
Memory: memoryLimit / 2,
|
|
|
|
|
MemorySwap: memoryLimit, // Memory + Swap
|
2015-10-20 10:51:43 -07:00
|
|
|
PortBindings: isAppContainer ? dockerPortBindings : { },
|
2015-10-19 16:00:40 -07:00
|
|
|
PublishAllPorts: false,
|
2016-01-15 11:28:14 -08:00
|
|
|
ReadonlyRootfs: !developmentMode, // see also Volumes in startContainer
|
2015-10-19 16:00:40 -07:00
|
|
|
RestartPolicy: {
|
2015-10-20 10:51:43 -07:00
|
|
|
"Name": isAppContainer ? "always" : "no",
|
2015-10-19 16:00:40 -07:00
|
|
|
"MaximumRetryCount": 0
|
|
|
|
|
},
|
|
|
|
|
CpuShares: 512, // relative to 1024 for system processes
|
2015-10-20 17:34:47 -07:00
|
|
|
VolumesFrom: isAppContainer ? null : [ app.containerId + ":rw" ],
|
2015-12-01 13:59:45 -08:00
|
|
|
NetworkMode: isolatedNetworkNs ? 'default' : ('container:' + app.containerId), // share network namespace with parent
|
|
|
|
|
Links: isolatedNetworkNs ? addons.getLinksSync(app, app.manifest.addons) : null, // links is redundant with --net=container
|
2015-10-19 16:00:40 -07:00
|
|
|
SecurityOpt: config.CLOUDRON ? [ "apparmor:docker-cloudron-app" ] : null // profile available only on cloudron
|
2015-10-20 17:34:47 -07:00
|
|
|
}
|
2015-10-19 16:00:40 -07:00
|
|
|
};
|
2015-11-10 12:47:48 -08:00
|
|
|
containerOptions = _.extend(containerOptions, options);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2015-11-10 12:47:48 -08:00
|
|
|
debugApp(app, 'Creating container for %s with options %j', app.manifest.dockerImage, containerOptions);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2015-10-19 16:00:40 -07:00
|
|
|
docker.createContainer(containerOptions, callback);
|
|
|
|
|
});
|
2015-10-19 11:40:19 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 21:33:53 -07:00
|
|
|
function createContainer(app, callback) {
|
2015-11-10 12:47:48 -08:00
|
|
|
createSubcontainer(app, app.id /* name */, null /* cmd */, { } /* options */, callback);
|
2015-10-19 21:33:53 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
function startContainer(containerId, callback) {
|
2015-10-19 15:51:02 -07:00
|
|
|
assert.strictEqual(typeof containerId, 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-10-19 11:40:19 -07:00
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
var container = docker.getContainer(containerId);
|
|
|
|
|
debug('Starting container %s', containerId);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
container.start(function (error) {
|
2015-10-19 15:39:26 -07:00
|
|
|
if (error && error.statusCode !== 304) return callback(new Error('Error starting container :' + error));
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
return callback(null);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
function stopContainer(containerId, callback) {
|
2015-10-19 15:51:02 -07:00
|
|
|
assert(!containerId || typeof containerId === 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
if (!containerId) {
|
|
|
|
|
debug('No previous container to stop');
|
2015-10-19 11:40:19 -07:00
|
|
|
return callback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
2015-10-19 15:39:26 -07:00
|
|
|
var container = docker.getContainer(containerId);
|
|
|
|
|
debug('Stopping container %s', containerId);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
var options = {
|
|
|
|
|
t: 10 // wait for 10 seconds before killing it
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
container.stop(options, function (error) {
|
|
|
|
|
if (error && (error.statusCode !== 304 && error.statusCode !== 404)) return callback(new Error('Error stopping container:' + error));
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
debug('Waiting for container ' + containerId);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
container.wait(function (error, data) {
|
|
|
|
|
if (error && (error.statusCode !== 304 && error.statusCode !== 404)) return callback(new Error('Error waiting on container:' + error));
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
debug('Container %s stopped with status code [%s]', containerId, data ? String(data.StatusCode) : '');
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
return callback(null);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
function deleteContainer(containerId, callback) {
|
2015-10-19 15:51:02 -07:00
|
|
|
assert(!containerId || typeof containerId === 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-10-19 18:48:56 -07:00
|
|
|
debug('deleting container %s', containerId);
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
if (containerId === null) return callback(null);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
2015-10-19 15:39:26 -07:00
|
|
|
var container = docker.getContainer(containerId);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
|
|
|
|
var removeOptions = {
|
|
|
|
|
force: true, // kill container if it's running
|
|
|
|
|
v: true // removes volumes associated with the container (but not host mounts)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
container.remove(removeOptions, function (error) {
|
|
|
|
|
if (error && error.statusCode === 404) return callback(null);
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
if (error) debug('Error removing container %s : %j', containerId, error);
|
|
|
|
|
|
2015-10-19 11:40:19 -07:00
|
|
|
callback(error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 18:48:56 -07:00
|
|
|
function deleteContainers(appId, callback) {
|
|
|
|
|
assert.strictEqual(typeof appId, 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
|
|
|
|
debug('deleting containers of %s', appId);
|
|
|
|
|
|
|
|
|
|
docker.listContainers({ all: 1, filters: JSON.stringify({ label: [ 'appId=' + appId ] }) }, function (error, containers) {
|
|
|
|
|
if (error) return callback(error);
|
|
|
|
|
|
|
|
|
|
async.eachSeries(containers, function (container, iteratorDone) {
|
|
|
|
|
deleteContainer(container.Id, iteratorDone);
|
|
|
|
|
}, callback);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-20 00:05:07 -07:00
|
|
|
function stopContainers(appId, callback) {
|
|
|
|
|
assert.strictEqual(typeof appId, 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
|
|
|
|
debug('stopping containers of %s', appId);
|
|
|
|
|
|
|
|
|
|
docker.listContainers({ all: 1, filters: JSON.stringify({ label: [ 'appId=' + appId ] }) }, function (error, containers) {
|
|
|
|
|
if (error) return callback(error);
|
|
|
|
|
|
|
|
|
|
async.eachSeries(containers, function (container, iteratorDone) {
|
|
|
|
|
stopContainer(container.Id, iteratorDone);
|
|
|
|
|
}, callback);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-19 15:39:26 -07:00
|
|
|
function deleteImage(manifest, callback) {
|
2015-10-19 15:51:02 -07:00
|
|
|
assert(!manifest || typeof manifest === 'object');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
2015-10-19 11:40:19 -07:00
|
|
|
var dockerImage = manifest ? manifest.dockerImage : null;
|
|
|
|
|
if (!dockerImage) return callback(null);
|
|
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
2016-01-21 14:59:24 -08:00
|
|
|
var removeOptions = {
|
|
|
|
|
force: false, // might be shared with another instance of this app
|
|
|
|
|
noprune: false // delete untagged parents
|
|
|
|
|
};
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2016-01-21 14:59:24 -08:00
|
|
|
// registry v1 used to pull down all *tags*. this meant that deleting image by tag was not enough (since that
|
|
|
|
|
// just removes the tag). we used to remove the image by id. this is not required anymore because aliases are
|
|
|
|
|
// not created anymore after https://github.com/docker/docker/pull/10571
|
|
|
|
|
docker.getImage(dockerImage).remove(removeOptions, function (error) {
|
|
|
|
|
if (error && error.statusCode === 404) return callback(null);
|
|
|
|
|
if (error && error.statusCode === 409) return callback(null); // another container using the image
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2016-01-21 14:59:24 -08:00
|
|
|
if (error) debug('Error removing image %s : %j', dockerImage, error);
|
2015-10-19 11:40:19 -07:00
|
|
|
|
2016-01-21 14:59:24 -08:00
|
|
|
callback(error);
|
2015-10-19 11:40:19 -07:00
|
|
|
});
|
|
|
|
|
}
|
2016-02-18 15:39:27 +01:00
|
|
|
|
|
|
|
|
function getContainerIdByIp(ip, callback) {
|
|
|
|
|
assert.strictEqual(typeof ip, 'string');
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
|
|
|
|
debug('get container by ip %s', ip);
|
|
|
|
|
|
|
|
|
|
var docker = exports.connection;
|
|
|
|
|
|
|
|
|
|
docker.listNetworks({}, function (error, result) {
|
|
|
|
|
if (error) return callback(error);
|
|
|
|
|
|
|
|
|
|
var bridge;
|
|
|
|
|
result.forEach(function (n) {
|
|
|
|
|
if (n.Name === 'bridge') bridge = n;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!bridge) return callback(new Error('Unable to find the bridge network'));
|
|
|
|
|
|
|
|
|
|
var containerId;
|
|
|
|
|
for (var id in bridge.Containers) {
|
|
|
|
|
if (bridge.Containers[id].IPv4Address.indexOf(ip) === 0) {
|
|
|
|
|
containerId = id;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!containerId) return callback(new Error('No container with that ip'));
|
|
|
|
|
|
|
|
|
|
debug('found container %s with ip %s', containerId, ip);
|
|
|
|
|
|
|
|
|
|
callback(null, containerId);
|
|
|
|
|
});
|
|
|
|
|
}
|