Read ca files and convert to string before passing to the docker daemon

Before this commit, the docker daemon would recieve the filename of the
.pem files, which would be interpreted as the body and would fail. This
commit ensures that the actual body of the pem files are sent to the
daemon.

Change-type: patch
Connects-to: #562
Signed-off-by: Cameron Diver <cameron@resin.io>
This commit is contained in:
Cameron Diver 2017-06-20 11:48:15 +01:00
parent 378f894da3
commit 5000febf72
No known key found for this signature in database
GPG Key ID: 40968281F12927FD
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. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). 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 ## [5.11.0] - 2017-06-19
### Added ### Added

View File

@ -202,57 +202,57 @@ module.exports = {
tmp.setGracefulCleanup(); tmp.setGracefulCleanup();
logs = ''; logs = '';
upload = function(token, username, url) { upload = function(token, username, url) {
var docker; return dockerUtils.getDocker(options).then(function(docker) {
docker = dockerUtils.getDocker(options); return parseInput(params, options).then(function(arg) {
return parseInput(params, options).then(function(arg) { var appName, build, imageName, source;
var appName, build, imageName, source; appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3];
appName = arg[0], build = arg[1], source = arg[2], imageName = arg[3]; return tmpNameAsync().then(function(bufferFile) {
return tmpNameAsync().then(function(bufferFile) { options = _.assign({}, options, {
options = _.assign({}, options, { appName: appName
appName: appName });
}); params = _.assign({}, params, {
params = _.assign({}, params, { source: source
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 Promise["try"](function() { return Promise["try"](function() {
return require('mz/fs').unlink(bufferFile); if (build) {
})["catch"](_.noop); 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) {
}).tap(function(arg) { var buildId, imageName;
var buildId, imageName; imageName = arg.image, buildId = arg.buildId;
imageName = arg.image, buildId = arg.buildId; logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName)));
logging.logSuccess(logStreams, "Successfully deployed image: " + (formatImageName(imageName))); return buildId;
return buildId; }).then(function(arg) {
}).then(function(arg) { var buildId, imageName;
var buildId, imageName; imageName = arg.image, buildId = arg.buildId;
imageName = arg.image, buildId = arg.buildId; if (logs === '' || (options.nologupload != null)) {
if (logs === '' || (options.nologupload != null)) { return '';
return ''; }
} logging.logInfo(logStreams, 'Uploading logs to dashboard...');
logging.logInfo(logStreams, 'Uploading logs to dashboard...'); return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs');
return Promise.join(logs, token, url, buildId, username, params.appName, uploadLogs)["return"]('Successfully uploaded logs'); }).then(function(msg) {
}).then(function(msg) { if (msg !== '') {
if (msg !== '') { return logging.logSuccess(logStreams, msg);
return logging.logSuccess(logStreams, msg); }
} }).asCallback(done);
}).asCallback(done); });
}; };
return Promise.join(resin.auth.getToken(), resin.auth.whoami(), resin.settings.get('resinUrl'), upload); 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) { exports.generateConnectOpts = generateConnectOpts = function(opts) {
var connectOpts; var Promise, fs;
connectOpts = {}; Promise = require('bluebird');
if ((opts.docker != null) && (opts.dockerHost == null)) { fs = require('mz/fs');
connectOpts.socketPath = opts.docker; return Promise["try"](function() {
} else if ((opts.dockerHost != null) && (opts.docker == null)) { var connectOpts;
connectOpts.host = opts.dockerHost; connectOpts = {};
connectOpts.port = opts.dockerPort || 2376; if ((opts.docker != null) && (opts.dockerHost == null)) {
} else if ((opts.docker != null) && (opts.dockerHost != null)) { connectOpts.socketPath = opts.docker;
throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue."); } else if ((opts.dockerHost != null) && (opts.docker == null)) {
} else { connectOpts.host = opts.dockerHost;
connectOpts.socketPath = '/var/run/docker.sock'; connectOpts.port = opts.dockerPort || 2376;
} } else if ((opts.docker != null) && (opts.dockerHost != null)) {
if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) { throw new Error("Both a local docker socket and docker host have been provided. Don't know how to continue.");
if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) { } else {
throw new Error('You must provide a CA, certificate and key in order to use TLS'); connectOpts.socketPath = '/var/run/docker.sock';
} }
connectOpts.ca = opts.ca; if ((opts.ca != null) || (opts.cert != null) || (opts.key != null)) {
connectOpts.cert = opts.cert; if (!((opts.ca != null) && (opts.cert != null) && (opts.key != null))) {
connectOpts.key = opts.key; throw new Error('You must provide a CA, certificate and key in order to use TLS');
} }
return connectOpts; 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) { exports.tarDirectory = tarDirectory = function(dir) {
@ -189,7 +197,7 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) {
return tarDirectory(params.source); return tarDirectory(params.source);
}).then(function(tarStream) { }).then(function(tarStream) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
var builder, connectOpts, hooks, opts; var hooks;
hooks = { hooks = {
buildSuccess: function(image) { buildSuccess: function(image) {
var doodle; var doodle;
@ -252,23 +260,25 @@ exports.runBuild = function(params, options, getBundleInfo, logStreams) {
return newStream.pipe(logThroughStream).pipe(cacheHighlightStream()).pipe(logStreams.build); return newStream.pipe(logThroughStream).pipe(cacheHighlightStream()).pipe(logStreams.build);
} }
}; };
connectOpts = generateConnectOpts(options); return generateConnectOpts(options).then(function(connectOpts) {
logging.logDebug(logStreams, 'Connecting with the following options:'); var builder, opts;
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')); logging.logDebug(logStreams, 'Connecting with the following options:');
builder = new dockerBuild.Builder(connectOpts); logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '));
opts = {}; builder = new dockerBuild.Builder(connectOpts);
if (options.tag != null) { opts = {};
opts['t'] = options.tag; if (options.tag != null) {
} opts['t'] = options.tag;
if (options.nocache != null) { }
opts['nocache'] = true; if (options.nocache != null) {
} opts['nocache'] = true;
if (options.buildArg != null) { }
opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) { if (options.buildArg != null) {
return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'"); opts['buildargs'] = parseBuildArgs(options.buildArg, function(arg) {
}); return logging.logWarn(logStreams, "Could not parse variable: '" + arg + "'");
} });
return builder.createBuildStream(opts, hooks, reject); }
return builder.createBuildStream(opts, hooks, reject);
});
}); });
}); });
}; };
@ -287,12 +297,13 @@ exports.bufferImage = function(docker, imageId, bufferFile) {
}; };
exports.getDocker = function(options) { exports.getDocker = function(options) {
var Docker, Promise, connectOpts; var Docker, Promise;
Docker = require('dockerode'); Docker = require('dockerode');
Promise = require('bluebird'); Promise = require('bluebird');
connectOpts = generateConnectOpts(options); return generateConnectOpts(options).then(function(connectOpts) {
connectOpts['Promise'] = Promise; connectOpts['Promise'] = Promise;
return new Docker(connectOpts); return new Docker(connectOpts);
});
}; };
hasQemu = function() { hasQemu = function() {

View File

@ -175,64 +175,65 @@ module.exports =
logs = '' logs = ''
upload = (token, username, url) -> upload = (token, username, url) ->
docker = dockerUtils.getDocker(options) dockerUtils.getDocker(options)
# Check input parameters .then (docker) ->
parseInput(params, options) # Check input parameters
.then ([appName, build, source, imageName]) -> parseInput(params, options)
tmpNameAsync() .then ([appName, build, source, imageName]) ->
.then (bufferFile) -> tmpNameAsync()
.then (bufferFile) ->
# Setup the build args for how the build routine expects them # Setup the build args for how the build routine expects them
options = _.assign({}, options, { appName }) options = _.assign({}, options, { appName })
params = _.assign({}, params, { source }) 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 -> Promise.try ->
require('mz/fs').unlink(bufferFile) if build
.catch(_.noop) dockerUtils.runBuild(params, options, getBundleInfo, logStreams)
.tap ({ image: imageName, buildId }) -> else
logging.logSuccess(logStreams, "Successfully deployed image: #{formatImageName(imageName)}") { image: imageName, log: '' }
return buildId .then ({ image: imageName, log: buildLogs }) ->
.then ({ image: imageName, buildId }) -> logging.logInfo(logStreams, 'Initializing deploy...')
if logs is '' or options.nologupload?
return ''
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( logging.logInfo(logStreams, 'Uploading logs to dashboard...')
logs
token Promise.join(
url logs
buildId token
username url
params.appName buildId
uploadLogs username
) params.appName
.return('Successfully uploaded logs') uploadLogs
.then (msg) -> )
logging.logSuccess(logStreams, msg) if msg isnt '' .return('Successfully uploaded logs')
.asCallback(done) .then (msg) ->
logging.logSuccess(logStreams, msg) if msg isnt ''
.asCallback(done)
Promise.join( Promise.join(
resin.auth.getToken() resin.auth.getToken()

View File

@ -70,36 +70,47 @@ exports.appendOptions = (opts) ->
] ]
exports.generateConnectOpts = generateConnectOpts = (opts) -> exports.generateConnectOpts = generateConnectOpts = (opts) ->
connectOpts = {} Promise = require('bluebird')
# Firsly need to decide between a local docker socket fs = require('mz/fs')
# and a host available over a host:port combo _ = require('lodash')
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'
# Now need to check if the user wants to connect over TLS Promise.try ->
# to the host 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... # Now need to check if the user wants to connect over TLS
if (opts.ca? or opts.cert? or opts.key?) # to the host
# 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
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) -> exports.tarDirectory = tarDirectory = (dir) ->
Promise = require('bluebird') Promise = require('bluebird')
@ -262,24 +273,25 @@ exports.runBuild = (params, options, getBundleInfo, logStreams) ->
.pipe(logStreams.build) .pipe(logStreams.build)
# Create a builder # Create a builder
connectOpts = generateConnectOpts(options) generateConnectOpts(options)
.then (connectOpts) ->
# Allow degugging output, hidden behind an env var # Allow degugging output, hidden behind an env var
logging.logDebug(logStreams, 'Connecting with the following options:') logging.logDebug(logStreams, 'Connecting with the following options:')
logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' ')) logging.logDebug(logStreams, JSON.stringify(connectOpts, null, ' '))
builder = new dockerBuild.Builder(connectOpts) builder = new dockerBuild.Builder(connectOpts)
opts = {} opts = {}
if options.tag? if options.tag?
opts['t'] = options.tag opts['t'] = options.tag
if options.nocache? if options.nocache?
opts['nocache'] = true opts['nocache'] = true
if options.buildArg? if options.buildArg?
opts['buildargs'] = parseBuildArgs options.buildArg, (arg) -> opts['buildargs'] = parseBuildArgs options.buildArg, (arg) ->
logging.logWarn(logStreams, "Could not parse variable: '#{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, # Given an image id or tag, export the image to a tar archive,
# gzip the result, and buffer it to disk. # gzip the result, and buffer it to disk.
@ -298,10 +310,12 @@ exports.bufferImage = (docker, imageId, bufferFile) ->
exports.getDocker = (options) -> exports.getDocker = (options) ->
Docker = require('dockerode') Docker = require('dockerode')
Promise = require('bluebird') Promise = require('bluebird')
connectOpts = generateConnectOpts(options)
# Use bluebird's promises generateConnectOpts(options)
connectOpts['Promise'] = Promise .then (connectOpts) ->
new Docker(connectOpts) # Use bluebird's promises
connectOpts['Promise'] = Promise
new Docker(connectOpts)
hasQemu = -> hasQemu = ->
fs = require('mz/fs') fs = require('mz/fs')