balena-cli/build/utils/docker.js
Cameron Diver 5000febf72
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>
2017-06-21 11:11:24 +01:00

375 lines
12 KiB
JavaScript

// Generated by CoffeeScript 1.12.6
var QEMU_BIN_NAME, QEMU_VERSION, cacheHighlightStream, copyQemu, generateConnectOpts, getQemuPath, hasQemu, installQemu, parseBuildArgs, platformNeedsQemu, tarDirectory;
QEMU_VERSION = 'v2.5.50-resin-execve';
QEMU_BIN_NAME = 'qemu-execve';
exports.appendOptions = function(opts) {
return opts.concat([
{
signature: 'docker',
parameter: 'docker',
description: 'Path to a local docker socket',
alias: 'P'
}, {
signature: 'dockerHost',
parameter: 'dockerHost',
description: 'The address of the host containing the docker daemon',
alias: 'h'
}, {
signature: 'dockerPort',
parameter: 'dockerPort',
description: 'The port on which the host docker daemon is listening',
alias: 'p'
}, {
signature: 'ca',
parameter: 'ca',
description: 'Docker host TLS certificate authority file'
}, {
signature: 'cert',
parameter: 'cert',
description: 'Docker host TLS certificate file'
}, {
signature: 'key',
parameter: 'key',
description: 'Docker host TLS key file'
}, {
signature: 'tag',
parameter: 'tag',
description: 'The alias to the generated image',
alias: 't'
}, {
signature: 'buildArg',
parameter: 'arg',
description: 'Set a build-time variable (eg. "-B \'ARG=value\'"). Can be specified multiple times.',
alias: 'B'
}, {
signature: 'nocache',
description: "Don't use docker layer caching when building",
boolean: true
}, {
signature: 'emulated',
description: 'Run an emulated build using Qemu',
boolean: true,
alias: 'e'
}
]);
};
exports.generateConnectOpts = generateConnectOpts = function(opts) {
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';
}
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) {
var Promise, fs, getFiles, klaw, pack, path, streamToPromise, tar;
Promise = require('bluebird');
tar = require('tar-stream');
klaw = require('klaw');
path = require('path');
fs = require('mz/fs');
streamToPromise = require('stream-to-promise');
getFiles = function() {
return streamToPromise(klaw(dir)).filter(function(item) {
return !item.stats.isDirectory();
}).map(function(item) {
return item.path;
});
};
pack = tar.pack();
return getFiles(dir).map(function(file) {
var relPath;
relPath = path.relative(path.resolve(dir), file);
return Promise.join(relPath, fs.stat(file), fs.readFile(file), function(filename, stats, data) {
return pack.entryAsync({
name: filename,
size: stats.size,
mode: stats.mode
}, data);
});
}).then(function() {
pack.finalize();
return pack;
});
};
cacheHighlightStream = function() {
var EOL, colors, es, extractArrowMessage;
colors = require('colors/safe');
es = require('event-stream');
EOL = require('os').EOL;
extractArrowMessage = function(message) {
var arrowTest, match;
arrowTest = /^\s*-+>\s*(.+)/i;
if ((match = arrowTest.exec(message))) {
return match[1];
} else {
return void 0;
}
};
return es.mapSync(function(data) {
var msg;
msg = extractArrowMessage(data);
if ((msg != null) && msg.toLowerCase() === 'using cache') {
data = colors.bgGreen.black(msg);
}
return data + EOL;
});
};
parseBuildArgs = function(args, onError) {
var _, buildArgs;
_ = require('lodash');
if (!_.isArray(args)) {
args = [args];
}
buildArgs = {};
args.forEach(function(str) {
var pair;
pair = /^([^\s]+?)=(.*)$/.exec(str);
if (pair != null) {
return buildArgs[pair[1]] = pair[2];
} else {
return onError(str);
}
});
return buildArgs;
};
exports.runBuild = function(params, options, getBundleInfo, logStreams) {
var Promise, dockerBuild, doodles, es, logging, logs, path, qemuPath, resolver, transpose;
Promise = require('bluebird');
dockerBuild = require('resin-docker-build');
resolver = require('resin-bundle-resolve');
es = require('event-stream');
doodles = require('resin-doodles');
transpose = require('docker-qemu-transpose');
path = require('path');
logging = require('../utils/logging');
if (params.source == null) {
params.source = '.';
}
logs = '';
qemuPath = '';
return Promise["try"](function() {
if (!(options.emulated && platformNeedsQemu())) {
return;
}
return hasQemu().then(function(present) {
if (!present) {
logging.logInfo(logStreams, 'Installing qemu for ARM emulation...');
return installQemu();
}
}).then(function() {
return copyQemu(params.source);
}).then(function(binPath) {
return qemuPath = path.relative(params.source, binPath);
});
}).then(function() {
return tarDirectory(params.source);
}).then(function(tarStream) {
return new Promise(function(resolve, reject) {
var hooks;
hooks = {
buildSuccess: function(image) {
var doodle;
if (options.tag != null) {
console.log("Tagging image as " + options.tag);
}
doodle = doodles.getDoodle();
console.log();
console.log(doodle);
console.log();
return resolve({
image: image,
log: logs + '\n' + doodle + '\n'
});
},
buildFailure: reject,
buildStream: function(stream) {
var buildThroughStream, logThroughStream, newStream;
if (options.emulated) {
logging.logInfo(logStreams, 'Running emulated build');
}
getBundleInfo(options).then(function(info) {
var arch, bundle, deviceType;
if (info == null) {
logging.logWarn(logStreams, 'Warning: No architecture/device type or application information provided.\n Dockerfile/project pre-processing will not be performed.');
return tarStream;
} else {
arch = info[0], deviceType = info[1];
bundle = new resolver.Bundle(tarStream, deviceType, arch);
return resolver.resolveBundle(bundle, resolver.getDefaultResolvers()).then(function(resolved) {
logging.logInfo(logStreams, "Building " + resolved.projectType + " project");
return resolved.tarStream;
});
}
}).then(function(buildStream) {
if (options.emulated && platformNeedsQemu()) {
return transpose.transposeTarStream(buildStream, {
hostQemuPath: qemuPath,
containerQemuPath: "/tmp/" + QEMU_BIN_NAME
});
} else {
return buildStream;
}
}).then(function(buildStream) {
return buildStream.pipe(stream);
})["catch"](reject);
logThroughStream = es.through(function(data) {
logs += data.toString();
return this.emit('data', data);
});
if (options.emulated && platformNeedsQemu()) {
buildThroughStream = transpose.getBuildThroughStream({
hostQemuPath: qemuPath,
containerQemuPath: "/tmp/" + QEMU_BIN_NAME
});
newStream = stream.pipe(buildThroughStream);
} else {
newStream = stream;
}
return newStream.pipe(logThroughStream).pipe(cacheHighlightStream()).pipe(logStreams.build);
}
};
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);
});
});
});
};
exports.bufferImage = function(docker, imageId, bufferFile) {
var Promise, image, imageMetadata, streamUtils;
Promise = require('bluebird');
streamUtils = require('./streams');
image = docker.getImage(imageId);
imageMetadata = image.inspectAsync();
return Promise.join(image.get(), imageMetadata.get('Size'), function(imageStream, imageSize) {
return streamUtils.buffer(imageStream, bufferFile).tap(function(bufferedStream) {
return bufferedStream.length = imageSize;
});
});
};
exports.getDocker = function(options) {
var Docker, Promise;
Docker = require('dockerode');
Promise = require('bluebird');
return generateConnectOpts(options).then(function(connectOpts) {
connectOpts['Promise'] = Promise;
return new Docker(connectOpts);
});
};
hasQemu = function() {
var fs;
fs = require('mz/fs');
return getQemuPath().then(fs.stat)["return"](true).catchReturn(false);
};
getQemuPath = function() {
var fs, path, resin;
resin = require('resin-sdk-preconfigured');
path = require('path');
fs = require('mz/fs');
return resin.settings.get('binDirectory').then(function(binDir) {
return fs.access(binDir)["catch"]({
code: 'ENOENT'
}, function() {
return fs.mkdir(binDir);
}).then(function() {
return path.join(binDir, QEMU_BIN_NAME);
});
});
};
platformNeedsQemu = function() {
var os;
os = require('os');
return os.platform() === 'linux';
};
installQemu = function() {
var fs, request, zlib;
request = require('request');
fs = require('fs');
zlib = require('zlib');
return getQemuPath().then(function(qemuPath) {
return new Promise(function(resolve, reject) {
var installStream, qemuUrl;
installStream = fs.createWriteStream(qemuPath);
qemuUrl = "https://github.com/resin-io/qemu/releases/download/" + QEMU_VERSION + "/" + QEMU_BIN_NAME + ".gz";
return request(qemuUrl).pipe(zlib.createGunzip()).pipe(installStream).on('error', reject).on('finish', resolve);
});
});
};
copyQemu = function(context) {
var binDir, binPath, fs, path;
path = require('path');
fs = require('mz/fs');
binDir = path.join(context, '.resin');
binPath = path.join(binDir, QEMU_BIN_NAME);
return fs.access(binDir)["catch"]({
code: 'ENOENT'
}, function() {
return fs.mkdir(binDir);
}).then(function() {
return getQemuPath();
}).then(function(qemu) {
return new Promise(function(resolve, reject) {
var read, write;
read = fs.createReadStream(qemu);
write = fs.createWriteStream(binPath);
return read.pipe(write).on('error', reject).on('finish', resolve);
});
}).then(function() {
return fs.chmod(binPath, '755');
})["return"](binPath);
};