mecano
Version:
Common functions for system deployment.
350 lines (342 loc) • 10.8 kB
JavaScript
// Generated by CoffeeScript 1.11.1
var curl, file, fs, path, ssh2fs, url,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
module.exports = function(options) {
var algo, protocols_ftp, protocols_http, ref, ref1, source_hash, source_url, stageDestination;
options.log({
message: "Entering download",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
if (!options.source) {
return callback(new Error("Missing source: " + options.source));
}
if (!options.target) {
return callback(new Error("Missing target: " + options.target));
}
if (/^file:\/\//.test(options.source)) {
options.source = options.source.substr(7);
}
stageDestination = null;
if (options.md5 != null) {
if ((ref = typeof options.md5) !== 'string' && ref !== 'boolean') {
return callback(new Error("Invalid MD5 Hash:" + options.md5));
}
algo = 'md5';
source_hash = options.md5;
} else if (options.sha1 != null) {
if ((ref1 = typeof options.sha1) !== 'string' && ref1 !== 'boolean') {
return callback(new Error("Invalid SHA-1 Hash:" + options.sha1));
}
algo = 'sha1';
source_hash = options.sha1;
} else {
algo = 'md5';
}
protocols_http = ['http:', 'https:'];
protocols_ftp = ['ftp:', 'ftps:'];
options.log({
message: "Using force: " + (JSON.stringify(options.force)),
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
source_url = url.parse(options.source);
if ((options.cache == null) && source_url.protocol === null) {
options.cache = false;
}
if (options.cache == null) {
options.cache = !!(options.cache_dir || options.cache_file);
}
this.call({
"if": typeof source_hash === 'string',
shy: true,
handler: function(_, callback) {
options.log({
message: "Shortcircuit check if provided hash match target",
level: 'WARN',
module: 'mecano/lib/file/download'
});
return file.hash(options.ssh, options.target, algo, (function(_this) {
return function(err, hash) {
if ((err != null ? err.code : void 0) === 'ENOENT') {
err = null;
}
return callback(err, source_hash === hash);
};
})(this));
}
}, function(err, end) {
if (!end) {
return;
}
options.log({
message: "Destination with valid signature, download aborted",
level: 'INFO',
module: 'mecano/lib/file/download'
});
return this.end();
});
this.cache({
"if": options.cache,
ssh: null,
source: options.source,
cache_dir: options.cache_dir,
cache_file: options.cache_file,
headers: options.headers,
md5: options.md5,
proxy: options.proxy,
location: options.location
}, function(err, cached, file) {
if (err) {
throw err;
}
if (options.cache) {
options.source = file;
}
return source_url = url.parse(options.source);
});
this.call(function(_, callback) {
return ssh2fs.stat(this.options.ssh, options.target, function(err, stat) {
if (err && err.code !== 'ENOENT') {
return callback(err);
}
if (stat != null ? stat.isDirectory() : void 0) {
options.log({
message: "Destination is a directory",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
options.target = path.join(options.target, path.basename(options.source));
}
stageDestination = options.target + "." + (Date.now()) + (Math.round(Math.random() * 1000));
return callback();
});
});
this.call({
"if": function() {
var ref2;
return ref2 = source_url.protocol, indexOf.call(protocols_http, ref2) >= 0;
},
handler: function() {
var cmd, fail, k;
options.log({
message: "HTTP Download",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
fail = options.fail ? "--fail" : '';
k = source_url.protocol === 'https:' ? '-k' : '';
cmd = "curl " + fail + " " + k + " -s " + options.source + " -o " + stageDestination;
if (options.proxy) {
cmd += " -x " + options.proxy;
}
options.log({
message: "Download file from url using curl",
level: 'INFO',
module: 'mecano/lib/file/download'
});
this.mkdir({
shy: true,
target: path.dirname(stageDestination)
});
this.execute({
cmd: cmd,
shy: true
});
this.call({
"if": typeof source_hash === 'string',
handler: function(_, callback) {
return file.hash(options.ssh, stageDestination, algo, (function(_this) {
return function(err, hash) {
if (source_hash !== hash) {
return callback(Error("Invalid downloaded checksum, found '" + hash + "' instead of '" + source_hash + "'"));
}
return callback();
};
})(this));
}
});
this.call(function(_, callback) {
return file.compare_hash(null, stageDestination, options.ssh, options.target, algo, function(err, match, hash1, hash2) {
if (!match) {
options.log({
message: "Hash dont match, source is '" + hash1 + "' and target is '" + hash2 + "'",
level: 'WARN',
module: 'mecano/lib/file/download'
});
}
if (match) {
options.log({
message: "Hash matches as '" + hash1 + "'",
level: 'INFO',
module: 'mecano/lib/file/download'
});
}
return callback(err, !match);
});
});
return this.remove({
unless: function() {
return this.status(-1);
},
shy: true,
target: stageDestination
});
}
});
this.call({
"if": function() {
var ref2;
return (ref2 = source_url.protocol, indexOf.call(protocols_http, ref2) < 0) && !options.ssh;
},
handler: function() {
options.log({
message: "File Download without ssh (with or without cache)",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
this.call(function(_, callback) {
return file.compare_hash(null, options.source, null, options.target, algo, function(err, match, hash1, hash2) {
if (!match) {
options.log({
message: "Hash dont match, source is '" + hash1 + "' and target is '" + hash2 + "'",
level: 'WARN',
module: 'mecano/lib/file/download'
});
}
if (match) {
options.log({
message: "Hash matches as '" + hash1 + "'",
level: 'INFO',
module: 'mecano/lib/file/download'
});
}
return callback(err, !match);
});
});
this.mkdir({
"if": function() {
return this.status(-1);
},
shy: true,
target: path.dirname(stageDestination)
});
return this.call({
"if": function() {
return this.status(-2);
},
handler: function(_, callback) {
var rs, ws;
rs = fs.createReadStream(options.source);
rs.on('error', function(err) {
options.log({
message: "No such source file: " + options.source + " (ssh is " + (JSON.stringify(!!options.ssh)) + ")",
level: 'ERROR',
module: 'mecano/lib/file/download'
});
err.message = 'No such source file';
return callback(err);
});
ws = fs.createWriteStream(stageDestination);
return rs.pipe(ws).on('close', callback).on('error', callback);
}
});
}
});
this.call({
"if": function() {
var ref2;
return (ref2 = source_url.protocol, indexOf.call(protocols_http, ref2) < 0) && options.ssh;
},
handler: function() {
options.log({
message: "File Download with ssh (with or without cache)",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
this.call(function(_, callback) {
return file.compare_hash(null, options.source, options.ssh, options.target, algo, function(err, match, hash1, hash2) {
if (!match) {
options.log({
message: "Hash dont match, source is '" + hash1 + "' and target is '" + hash2 + "'",
level: 'WARN',
module: 'mecano/lib/file/download'
});
}
if (match) {
options.log({
message: "Hash matches as '" + hash1 + "'",
level: 'INFO',
module: 'mecano/lib/file/download'
});
}
return callback(err, !match);
});
});
this.mkdir({
"if": function() {
return this.status(-1);
},
shy: true,
target: path.dirname(stageDestination)
});
return this.call({
"if": function() {
return this.status(-2);
},
handler: function(_, callback) {
var rs;
options.log({
message: "Local source: '" + options.source + "'",
level: 'INFO',
module: 'mecano/lib/file/download'
});
options.log({
message: "Remote target: '" + stageDestination + "'",
level: 'INFO',
module: 'mecano/lib/file/download'
});
rs = fs.createReadStream(options.source);
rs.on('error', function(err) {
return console.log('rs on error', err);
});
return ssh2fs.writeFile(options.ssh, stageDestination, rs, function(err) {
if (err) {
options.log("Upload failed from local to remote");
}
return callback(err);
});
}
});
}
});
return this.call(function() {
options.log({
message: "Unstage downloaded file",
level: 'DEBUG',
module: 'mecano/lib/file/download'
});
this.move({
"if": this.status(),
source: stageDestination,
target: options.target
});
this.chmod({
target: options.target,
mode: options.mode,
"if": options.mode != null
});
return this.chown({
target: options.target,
uid: options.uid,
gid: options.gid,
"if": (options.uid != null) || (options.gid != null)
});
});
};
fs = require('fs');
ssh2fs = require('ssh2-fs');
path = require('path');
url = require('url');
curl = require('../misc/curl');
file = require('../misc/file');