Merge pull request #563 from resin-io/562-fix-tls-connect

Read ca files and convert to string before passing to the docker daemon
This commit is contained in:
CameronDiver 2017-06-22 08:41:10 +01:00 committed by GitHub
commit 26d123b33d
5 changed files with 219 additions and 189 deletions

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## Fixed
- Ensure to send .pem file contents rather than filename to docker daemon
## [5.11.0] - 2017-06-19
### Added

View File

@ -202,57 +202,57 @@ module.exports = {
tmp.setGracefulCleanup();
logs = '';
upload = function(token, username, url) {
var docker;
docker = dockerUtils.getDocker(options);
return parseInput(params, options).then(function(arg) {
var appName, build, imageName, source;
appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3];
return tmpNameAsync().then(function(bufferFile) {
options = _.assign({}, options, {
appName: appName
});
params = _.assign({}, params, {
source: source
});
return Promise["try"](function() {
if (build) {
return dockerUtils.runBuild(params, options, getBundleInfo, logStreams);
} else {
return {
image: imageName,
log: ''
};
}
}).then(function(arg1) {
var buildLogs, imageName;
imageName = arg1.image, buildLogs = arg1.log;
logging.logInfo(logStreams, 'Initializing deploy...');
logs = buildLogs;
return Promise.all([dockerUtils.bufferImage(docker, imageName, bufferFile), token, username, url, params.appName, logStreams]).spread(performUpload);
})["finally"](function() {
return dockerUtils.getDocker(options).then(function(docker) {
return parseInput(params, options).then(function(arg) {
var appName, build, imageName, source;
appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3];
return tmpNameAsync().then(function(bufferFile) {
options = _.assign({}, options, {
appName: appName
});
params = _.assign({}, params, {
source: source
});
return Promise["try"](function() {
return require('mz/fs').unlink(bufferFile);
})["catch"](_.noop);
if (build) {
return dockerUtils.runBuild(params, options, getBundleInfo, logStreams);
} else {
return {
image: imageName,
log: ''
};
}
}).then(function(arg1) {
var buildLogs, imageName;
imageName = arg1.image, buildLogs = arg1.log;
logging.logInfo(logStreams, 'Initializing deploy...');
logs = buildLogs;
return Promise.all([dockerUtils.bufferImage(docker, imageName, bufferFile), token, username, url, params.appName, logStreams]).spread(performUpload);
})["finally"](function() {
return Promise["try"](function() {
return require('mz/fs').unlink(bufferFile);
})["catch"](_.noop);
});
});
});
}).tap(function(arg) {
var buildId, imageName;
imageName = arg.image, buildId = arg.buildId;
logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName)));
return buildId;
}).then(function(arg) {
var buildId, imageName;
imageName = arg.image, buildId = arg.buildId;
if (logs === '' || (options.nologupload != null)) {
return '';
}
logging.logInfo(logStreams, 'Uploading logs to dashboard...');
return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs');
}).then(function(msg) {
if (msg !== '') {
return logging.logSuccess(logStreams, msg);
}
}).asCallback(done);
}).tap(function(arg) {
var buildId, imageName;
imageName = arg.image, buildId = arg.buildId;
logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName)));
return buildId;
}).then(function(arg) {
var buildId, imageName;
imageName = arg.image, buildId = arg.buildId;
if (logs === '' || (options.nologupload != null)) {
return '';
}
logging.logInfo(logStreams, 'Uploading logs to dashboard...');
return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs');
}).then(function(msg) {
if (msg !== '') {
return logging.logSuccess(logStreams, msg);
}
}).asCallback(done);
});
};
return Promise.join(resin.auth.getToken(), resin.auth.whoami(), resin.settings.get('resinUrl'), upload);
}

View File

@ -58,27 +58,35 @@ exports.appendOptions = function(opts) {
};
exports.generateConnectOpts = generateConnectOpts = function(opts) {
var connectOpts;
connectOpts = {};
if ((opts.docker != null) && (opts.dockerHost == null)) {
connectOpts.socketPath = opts.docker;
} else if ((opts.dockerHost != null) && (opts.docker == null)) {
connectOpts.host = opts.dockerHost;
connectOpts.port = opts.dockerPort || 2376;
} else if ((opts.docker != null) && (opts.dockerHost != null)) {
throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.");
} else {
connectOpts.socketPath = '/var/run/docker.sock';
}
if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) {
if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) {
throw new Error('You must provide a CA, certificate and key in order to use TLS');
var Promise, fs;
Promise = require('bluebird');
fs = require('mz/fs');
return Promise["try"](function() {
var connectOpts;
connectOpts = {};
if ((opts.docker != null) && (opts.dockerHost == null)) {
connectOpts.socketPath = opts.docker;
} else if ((opts.dockerHost != null) && (opts.docker == null)) {
connectOpts.host = opts.dockerHost;
connectOpts.port = opts.dockerPort || 2376;
} else if ((opts.docker != null) && (opts.dockerHost != null)) {
throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.");
} else {
connectOpts.socketPath = '/var/run/docker.sock';
}
connectOpts.ca = opts.ca;
connectOpts.cert = opts.cert;
connectOpts.key = opts.key;
}
return connectOpts;
if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) {
if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) {
throw new Error('You must provide a CA, certificate and key in order to use TLS');
}
return Promise.all([fs.readFile(opts.ca), fs.readFile(opts.cert), fs.readFile(opts.key)]).spread(function(ca, cert, key) {
connectOpts.ca = ca.toString();
connectOpts.cert = cert.toString();
connectOpts.key = key.toString();
return connectOpts;
});
}
return connectOpts;
});
};
exports.tarDirectory = tarDirectory = function(dir) {
@ -189,7 +197,7 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) {
return tarDirectory(params.source);
}).then(function(tarStream) {
return new Promise(function(resolve, reject) {
var builder, connectOpts, hooks, opts;
var hooks;
hooks = {
buildSuccess: function(image) {
var doodle;
@ -252,23 +260,25 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) {
return newStream.pipe(logThroughStream).pipe(cacheHighlightStream()).pipe(logStreams.build);
}
};
connectOpts = generateConnectOpts(options);
logging.logDebug(logStreams, 'Connecting with the following options:');
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '));
builder = new dockerBuild.Builder(connectOpts);
opts = {};
if (options.tag != null) {
opts['t'] = options.tag;
}
if (options.nocache != null) {
opts['nocache'] = true;
}
if (options.buildArg != null) {
opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) {
return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'");
});
}
return builder.createBuildStream(opts, hooks, reject);
return generateConnectOpts(options).then(function(connectOpts) {
var builder, opts;
logging.logDebug(logStreams, 'Connecting with the following options:');
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '));
builder = new dockerBuild.Builder(connectOpts);
opts = {};
if (options.tag != null) {
opts['t'] = options.tag;
}
if (options.nocache != null) {
opts['nocache'] = true;
}
if (options.buildArg != null) {
opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) {
return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'");
});
}
return builder.createBuildStream(opts, hooks, reject);
});
});
});
};
@ -287,12 +297,13 @@ exports.bufferImage = function(docker, imageId, bufferFile) {
};
exports.getDocker = function(options) {
var Docker, Promise, connectOpts;
var Docker, Promise;
Docker = require('dockerode');
Promise = require('bluebird');
connectOpts = generateConnectOpts(options);
connectOpts['Promise'] = Promise;
return new Docker(connectOpts);
return generateConnectOpts(options).then(function(connectOpts) {
connectOpts['Promise'] = Promise;
return new Docker(connectOpts);
});
};
hasQemu = function() {

View File

@ -175,64 +175,65 @@ module.exports =
logs = ''
upload = (token, username, url) ->
docker = dockerUtils.getDocker(options)
# Check input parameters
parseInput(params, options)
.then ([appName, build, source, imageName]) ->
tmpNameAsync()
.then (bufferFile) ->
dockerUtils.getDocker(options)
.then (docker) ->
# Check input parameters
parseInput(params, options)
.then ([appName, build, source, imageName]) ->
tmpNameAsync()
.then (bufferFile) ->
# Setup the build args for how the build routine expects them
options = _.assign({}, options, { appName })
params = _.assign({}, params, { source })
# Setup the build args for how the build routine expects them
options = _.assign({}, options, { appName })
params = _.assign({}, params, { source })
Promise.try ->
if build
dockerUtils.runBuild(params, options, getBundleInfo, logStreams)
else
{ image: imageName, log: '' }
.then ({ image: imageName, log: buildLogs }) ->
logging.logInfo(logStreams, 'Initializing deploy...')
logs = buildLogs
Promise.all [
dockerUtils.bufferImage(docker, imageName, bufferFile)
token
username
url
params.appName
logStreams
]
.spread(performUpload)
.finally ->
# If the file was never written to (for instance because an error
# has occured before any data was written) this call will throw an
# ugly error, just suppress it
Promise.try ->
require('mz/fs').unlink(bufferFile)
.catch(_.noop)
.tap ({ image: imageName, buildId }) ->
logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}")
return buildId
.then ({ image: imageName, buildId }) ->
if logs is '' or options.nologupload?
return ''
if build
dockerUtils.runBuild(params, options, getBundleInfo, logStreams)
else
{ image: imageName, log: '' }
.then ({ image: imageName, log: buildLogs }) ->
logging.logInfo(logStreams, 'Initializing deploy...')
logging.logInfo(logStreams, 'Uploading logs to dashboard...')
logs = buildLogs
Promise.all [
dockerUtils.bufferImage(docker, imageName, bufferFile)
token
username
url
params.appName
logStreams
]
.spread(performUpload)
.finally ->
# If the file was never written to (for instance because an error
# has occured before any data was written) this call will throw an
# ugly error, just suppress it
Promise.try ->
require('mz/fs').unlink(bufferFile)
.catch(_.noop)
.tap ({ image: imageName, buildId }) ->
logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}")
return buildId
.then ({ image: imageName, buildId }) ->
if logs is '' or options.nologupload?
return ''
Promise.join(
logs
token
url
buildId
username
params.appName
uploadLogs
)
.return('Successfully uploaded logs')
.then (msg) ->
logging.logSuccess(logStreams, msg) if msg isnt ''
.asCallback(done)
logging.logInfo(logStreams, 'Uploading logs to dashboard...')
Promise.join(
logs
token
url
buildId
username
params.appName
uploadLogs
)
.return('Successfully uploaded logs')
.then (msg) ->
logging.logSuccess(logStreams, msg) if msg isnt ''
.asCallback(done)
Promise.join(
resin.auth.getToken()

View File

@ -70,36 +70,47 @@ exports.appendOptions = (opts) ->
]
exports.generateConnectOpts = generateConnectOpts = (opts) ->
connectOpts = {}
# Firsly need to decide between a local docker socket
# and a host available over a host:port combo
if opts.docker? and not opts.dockerHost?
# good, local docker socket
connectOpts.socketPath = opts.docker
else if opts.dockerHost? and not opts.docker?
# Good a host is provided, and local socket isn't
connectOpts.host = opts.dockerHost
connectOpts.port = opts.dockerPort || 2376
else if opts.docker? and opts.dockerHost?
# Both provided, no obvious way to continue
throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.")
else
# None provided, assume default docker local socket
connectOpts.socketPath = '/var/run/docker.sock'
Promise = require('bluebird')
fs = require('mz/fs')
_ = require('lodash')
# Now need to check if the user wants to connect over TLS
# to the host
Promise.try ->
connectOpts = {}
# Firsly need to decide between a local docker socket
# and a host available over a host:port combo
if opts.docker? and not opts.dockerHost?
# good, local docker socket
connectOpts.socketPath = opts.docker
else if opts.dockerHost? and not opts.docker?
# Good a host is provided, and local socket isn't
connectOpts.host = opts.dockerHost
connectOpts.port = opts.dockerPort || 2376
else if opts.docker? and opts.dockerHost?
# Both provided, no obvious way to continue
throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.")
else
# None provided, assume default docker local socket
connectOpts.socketPath = '/var/run/docker.sock'
# If any are set...
if (opts.ca? or opts.cert? or opts.key?)
# but not all
if not (opts.ca? and opts.cert? and opts.key?)
throw new Error('You must provide a CA, certificate and key in order to use TLS')
connectOpts.ca = opts.ca
connectOpts.cert = opts.cert
connectOpts.key = opts.key
# Now need to check if the user wants to connect over TLS
# to the host
return connectOpts
# If any are set...
if (opts.ca? or opts.cert? or opts.key?)
# but not all
if not (opts.ca? and opts.cert? and opts.key?)
throw new Error('You must provide a CA, certificate and key in order to use TLS')
certBodies = {
ca: fs.readFile(opts.ca, 'utf-8')
cert: fs.readFile(opts.cert, 'utf-8')
key: fs.readFile(opts.key, 'utf-8')
}
return Promise.props(certBodies)
.then (toMerge) ->
_.merge(connectOpts, toMerge)
return connectOpts
exports.tarDirectory = tarDirectory = (dir) ->
Promise = require('bluebird')
@ -262,24 +273,25 @@ exports.runBuild = (params, options, getBundleInfo, logStreams) ->
.pipe(logStreams.build)
# Create a builder
connectOpts = generateConnectOpts(options)
generateConnectOpts(options)
.then (connectOpts) ->
# Allow degugging output, hidden behind an env var
logging.logDebug(logStreams, 'Connecting with the following options:')
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '))
# Allow degugging output, hidden behind an env var
logging.logDebug(logStreams, 'Connecting with the following options:')
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '))
builder = new dockerBuild.Builder(connectOpts)
opts = {}
builder = new dockerBuild.Builder(connectOpts)
opts = {}
if options.tag?
opts['t'] = options.tag
if options.nocache?
opts['nocache'] = true
if options.buildArg?
opts['buildargs'] = parseBuildArgs options.buildArg, (arg) ->
logging.logWarn(logStreams, "Could not parse variable: '#{arg}'")
if options.tag?
opts['t'] = options.tag
if options.nocache?
opts['nocache'] = true
if options.buildArg?
opts['buildargs'] = parseBuildArgs options.buildArg, (arg) ->
logging.logWarn(logStreams, "Could not parse variable: '#{arg}'")
builder.createBuildStream(opts, hooks, reject)
builder.createBuildStream(opts, hooks, reject)
# Given an image id or tag, export the image to a tar archive,
# gzip the result, and buffer it to disk.
@ -298,10 +310,12 @@ exports.bufferImage = (docker, imageId, bufferFile) ->
exports.getDocker = (options) ->
Docker = require('dockerode')
Promise = require('bluebird')
connectOpts = generateConnectOpts(options)
# Use bluebird's promises
connectOpts['Promise'] = Promise
new Docker(connectOpts)
generateConnectOpts(options)
.then (connectOpts) ->
# Use bluebird's promises
connectOpts['Promise'] = Promise
new Docker(connectOpts)
hasQemu = ->
fs = require('mz/fs')