mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'deploy-delete'
This commit is contained in:
@@ -1,16 +1,192 @@
|
||||
// URL parsing and validation
|
||||
// RPC to server (endpoint, arguments)
|
||||
// see if RPC requires password
|
||||
// prompt for password
|
||||
// send RPC with or without password as required
|
||||
|
||||
var crypto = require('crypto');
|
||||
var tty = require('tty');
|
||||
var request = require('request');
|
||||
var qs = require('querystring');
|
||||
var path = require('path');
|
||||
var files = require('../lib/files.js');
|
||||
var _ = require('../lib/third/underscore.js');
|
||||
|
||||
//
|
||||
// configuration
|
||||
//
|
||||
|
||||
exports.HOSTNAME = 'deploy.skybreakplatform.com';
|
||||
var DEPLOY_HOSTNAME = 'deploy.skybreakplatform.com';
|
||||
|
||||
// available RPCs are: deploy (with set-password), delete, logs,
|
||||
// mongo_cred. each RPC might require a password, which we
|
||||
// interactively prompt for here.
|
||||
|
||||
var sky_rpc = function (rpc_name, method, site, query_params, callback) {
|
||||
var url = "http://" + DEPLOY_HOSTNAME + '/' + rpc_name + '/' + site;
|
||||
|
||||
if (!_.isEmpty(query_params))
|
||||
url += '?' + qs.stringify(query_params);
|
||||
|
||||
var r = request({method: method, url: url}, function (error, response, body) {
|
||||
if (error || ((response.statusCode !== 200)
|
||||
&& (response.statusCode !== 201)))
|
||||
// pass some non-falsy error back to callback
|
||||
callback(error || response.statusCode, body);
|
||||
else
|
||||
callback(null, body);
|
||||
});
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
var deploy_app = function (url, app_dir, opt_set_password) {
|
||||
var parsed_url = parse_url(url);
|
||||
|
||||
// a bit contorted here to make sure we ask for the password before
|
||||
// launching the slow bundle process.
|
||||
|
||||
with_password(parsed_url.hostname, function (password) {
|
||||
if (opt_set_password)
|
||||
get_new_password(function (set_password) {
|
||||
bundle_and_deploy(parsed_url.hostname, app_dir, password, set_password);
|
||||
});
|
||||
else
|
||||
bundle_and_deploy(parsed_url.hostname, app_dir, password);
|
||||
});
|
||||
};
|
||||
|
||||
var bundle_and_deploy = function (site, app_dir, password, set_password) {
|
||||
var build_dir = path.join(app_dir, '.skybreak/local/build_tar');
|
||||
var bundle_path = path.join(build_dir, 'bundle');
|
||||
var bundle_opts = { skip_dev_bundle: true };
|
||||
|
||||
process.stdout.write('Deploying to ' + site + '. Bundling ... ');
|
||||
require('../lib/bundler.js').bundle(app_dir, bundle_path, bundle_opts);
|
||||
|
||||
process.stdout.write('uploading ... ');
|
||||
|
||||
var opts = {};
|
||||
if (password) opts.password = password;
|
||||
if (set_password) opts.set_password = set_password;
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
var tar = spawn('tar', ['czf', '-', 'bundle'], {cwd: build_dir});
|
||||
|
||||
var rpc = sky_rpc('deploy', 'POST', site, opts, function (err, body) {
|
||||
// XXX this is gross. maybe some way to automate?
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
|
||||
if (err) {
|
||||
process.stderr.write("\nError deploying application: " + body + "\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write('done.\n');
|
||||
process.stdout.write('Now serving at ' + site + '\n');
|
||||
|
||||
files.rm_recursive(build_dir);
|
||||
|
||||
if (!site.match('skybreakplatform.com')) {
|
||||
var dns = require('dns');
|
||||
dns.resolve(site, 'CNAME', function (err, cnames) {
|
||||
if (err || cnames[0] !== 'origin.skybreakplatform.com') {
|
||||
dns.resolve(site, 'A', function (err, addresses) {
|
||||
if (err || addresses[0] !== '107.22.210.133') {
|
||||
process.stdout.write('-------------\n');
|
||||
process.stdout.write("You've deployed to a custom domain.\n");
|
||||
process.stdout.write("Please be sure to CNAME your hostname to origin.skybreakplatform.com,\n");
|
||||
process.stdout.write("or set an A record to 107.22.210.133.\n");
|
||||
process.stdout.write('-------------\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tar.stdout.pipe(rpc);
|
||||
};
|
||||
|
||||
var delete_app = function (url) {
|
||||
var parsed_url = parse_url(url);
|
||||
|
||||
with_password(parsed_url.hostname, function (password) {
|
||||
var opts = {};
|
||||
if (password) opts.password = password;
|
||||
|
||||
sky_rpc('deploy', 'DELETE', parsed_url.hostname, opts, function (err, body) {
|
||||
process.stdin.destroy(); // clean up after with_password
|
||||
|
||||
if (err) {
|
||||
process.stderr.write("Error deleting application: " + body + "\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write("Deleted.\n");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// either print the mongo credential (just_credential is true) or open
|
||||
// a mongo shell.
|
||||
var mongo = function (url, just_credential) {
|
||||
var parsed_url = parse_url(url);
|
||||
|
||||
with_password(parsed_url.hostname, function (password) {
|
||||
var opts = {};
|
||||
if (password) opts.password = password;
|
||||
|
||||
sky_rpc('mongo', 'GET', parsed_url.hostname, opts, function (err, body) {
|
||||
if (err) {
|
||||
process.stderr.write(body + "\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (just_credential) {
|
||||
// just print the URL
|
||||
process.stdout.write(body + "\n");
|
||||
|
||||
// only do this if we're printing the URL. Don't do it if
|
||||
// we're running the mongo shell, since that will close off
|
||||
// stdin for the shell.
|
||||
process.stdin.destroy(); // clean up after with_password
|
||||
|
||||
} else {
|
||||
// pause stdin so we don't try to read it while mongo is
|
||||
// running.
|
||||
process.stdin.pause();
|
||||
run_mongo_shell(body);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var logs = function (url) {
|
||||
var parsed_url = parse_url(url);
|
||||
|
||||
with_password(parsed_url.hostname, function (password) {
|
||||
var opts = {};
|
||||
if (password) opts.password = password;
|
||||
|
||||
sky_rpc('logs', 'GET', parsed_url.hostname, opts, function (err, body) {
|
||||
process.stdin.destroy(); // clean up after with_password
|
||||
|
||||
if (err) {
|
||||
process.stderr.write(body + '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write(body);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// accepts www.host.com, defaults domain to skybreakplatform, defaults
|
||||
// protocol to http.
|
||||
// protocol to http. on bad URL, prints error and exits the process.
|
||||
//
|
||||
// XXX shared w/ proxy.js
|
||||
exports.parse_url = function (url) {
|
||||
var parse_url = function (url) {
|
||||
if (!url.match(':\/\/'))
|
||||
url = 'http://' + url;
|
||||
|
||||
@@ -21,25 +197,41 @@ exports.parse_url = function (url) {
|
||||
if (parsed.hostname && !parsed.hostname.match(/\./))
|
||||
parsed.hostname += '.skybreakplatform.com';
|
||||
|
||||
return parsed;
|
||||
};
|
||||
|
||||
exports.validate_url = function (url) {
|
||||
if (!url.hostname) {
|
||||
if (!parsed.hostname) {
|
||||
process.stdout.write(
|
||||
"Please specify a domain to connect to, such as www.example.com or\n" +
|
||||
"http://www.example.com/\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (url.pathname != '/' || url.hash || url.query) {
|
||||
if (parsed.pathname != '/' || parsed.hash || parsed.query) {
|
||||
process.stdout.write(
|
||||
"Sorry, Skybreak does not yet support specific path URLs, such as\n" +
|
||||
"http://www.example.com/blog . Please specify the root of a domain.\n");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
};
|
||||
|
||||
var run_mongo_shell = function (url) {
|
||||
var mongo_path = path.join(files.get_dev_bundle(), 'mongodb/bin/mongo');
|
||||
var mongo_url = require('url').parse(url);
|
||||
var auth = mongo_url.auth && mongo_url.auth.split(':');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var args = [];
|
||||
if (auth) args.push('-u', auth[0]);
|
||||
if (auth) args.push('-p', auth[1]);
|
||||
args.push(mongo_url.hostname + ':' + mongo_url.port + mongo_url.pathname);
|
||||
|
||||
var proc = spawn(mongo_path,
|
||||
args,
|
||||
{ customFds: [0, 1, 2] });
|
||||
proc.on('exit', function () {
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
});
|
||||
};
|
||||
|
||||
// hash the password so we never send plaintext over the wire. Doesn't
|
||||
// actually make us more secure, but it means we won't leak a user's
|
||||
@@ -96,41 +288,35 @@ var read_password = function (callback) {
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Check if a particular endpoint requires a password. If so, prompt for
|
||||
// it.
|
||||
//
|
||||
// takes an endpoint name and callback function(password). This is
|
||||
// always called exactly once. If no password is needed, password will
|
||||
// be undefined.
|
||||
exports.maybe_password = function (endpoint, callback) {
|
||||
var check_url = "http://" + exports.HOSTNAME + "/has_password/" + endpoint;
|
||||
// takes an site name and callback function(password). This is always
|
||||
// called exactly once. Calls callback with the entered password, or
|
||||
// undefined if no password is required.
|
||||
var with_password = function (site, callback) {
|
||||
var check_url = "http://" + DEPLOY_HOSTNAME + "/has_password/" + site;
|
||||
|
||||
request(check_url, function (error, response, body) {
|
||||
if (error || response.statusCode !== 200) {
|
||||
// XXX more fine grained error handling
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX in theory we should JSON parse the result, and use that. But
|
||||
// we happen to know we'll only ever get 'true' or 'false' if we got
|
||||
// a 200, so don't bother.
|
||||
|
||||
if (body === "false") {
|
||||
} else if (body === "false") {
|
||||
// XXX in theory we should JSON parse the result, and use
|
||||
// that. But we happen to know we'll only ever get 'true' or
|
||||
// 'false' if we got a 200, so don't bother.
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
process.stdout.write("Password: ");
|
||||
read_password(callback);
|
||||
} else {
|
||||
process.stdout.write("Password: ");
|
||||
read_password(callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Prompts for a new password, asking you twice so you don't typo
|
||||
// it. Keeps prompting you until you have two that match.
|
||||
exports.get_new_password = function (callback) {
|
||||
var get_new_password = function (callback) {
|
||||
process.stdout.write("New Password: ");
|
||||
read_password(function (p1) {
|
||||
process.stdout.write("Confirm Password: ");
|
||||
@@ -140,7 +326,12 @@ exports.get_new_password = function (callback) {
|
||||
return;
|
||||
}
|
||||
process.stdout.write("Passwords do not match! Try again.\n");
|
||||
exports.get_new_password(callback);
|
||||
get_new_password(callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.deploy_app = deploy_app;
|
||||
exports.delete_app = delete_app;
|
||||
exports.mongo = mongo;
|
||||
exports.logs = logs;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var files = require('../lib/files.js');
|
||||
var path = require('path');
|
||||
var _ = require('../lib/third/underscore.js');
|
||||
var deploy = require('./deploy');
|
||||
|
||||
var usage = function() {
|
||||
process.stdout.write(
|
||||
@@ -354,25 +355,6 @@ Commands.push({
|
||||
}
|
||||
});
|
||||
|
||||
var run_mongo_shell = function (url) {
|
||||
var mongo_path = path.join(files.get_dev_bundle(), 'mongodb/bin/mongo');
|
||||
var mongo_url = require('url').parse(url);
|
||||
var auth = mongo_url.auth && mongo_url.auth.split(':');
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var args = [];
|
||||
if (auth) args.push('-u', auth[0]);
|
||||
if (auth) args.push('-p', auth[1]);
|
||||
args.push(mongo_url.hostname + ':' + mongo_url.port + mongo_url.pathname);
|
||||
|
||||
var proc = spawn(mongo_path,
|
||||
args,
|
||||
{ customFds: [0, 1, 2] });
|
||||
proc.on('exit', function () {
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
});
|
||||
};
|
||||
|
||||
Commands.push({
|
||||
name: "mongo",
|
||||
help: "Connect to the Mongo database for the specified site",
|
||||
@@ -428,55 +410,7 @@ Commands.push({
|
||||
|
||||
} else if (new_argv._.length === 2) {
|
||||
// remote mode
|
||||
var deploy = require('./deploy');
|
||||
var url = deploy.parse_url(new_argv._[1]);
|
||||
deploy.validate_url(url);
|
||||
|
||||
deploy.maybe_password(url.hostname, function (password) {
|
||||
|
||||
var options = {
|
||||
host: deploy.HOSTNAME,
|
||||
port: 80,
|
||||
path: '/mongo/' + url.hostname,
|
||||
};
|
||||
if (password) {
|
||||
options.path += '?password=' + password;
|
||||
}
|
||||
var data = '';
|
||||
|
||||
var req = require('http').get(options, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) { data += chunk; });
|
||||
res.on('end', function () {
|
||||
if (res.statusCode == 200) {
|
||||
if (new_argv.url) {
|
||||
console.log(data);
|
||||
|
||||
// only do this if we're printing the URL. Don't do it
|
||||
// if we're running the mongo shell, since that will
|
||||
// close off stdin for the shell.
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
} else {
|
||||
// pause stdin so we don't try to read it while mongo is
|
||||
// running.
|
||||
process.stdin.pause();
|
||||
run_mongo_shell(data);
|
||||
}
|
||||
|
||||
} else {
|
||||
process.stderr.write(data);
|
||||
process.stderr.write("\n");
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function(e) {
|
||||
console.log(e);
|
||||
console.log("Error connecting to Skybreak: " + e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
deploy.mongo(new_argv._[1], new_argv.url);
|
||||
|
||||
} else {
|
||||
// usage
|
||||
@@ -494,6 +428,9 @@ Commands.push({
|
||||
.boolean('password')
|
||||
.alias('password', 'P')
|
||||
.describe('password', 'set a password for the deployment')
|
||||
.boolean('delete')
|
||||
.alias('delete', 'D')
|
||||
.describe('delete', "permanently delete this project and its data from Skybreak")
|
||||
.usage(
|
||||
"Usage: skybreak deploy <site>\n" +
|
||||
"\n" +
|
||||
@@ -508,95 +445,12 @@ Commands.push({
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var deploy = require('./deploy');
|
||||
|
||||
var url = deploy.parse_url(new_argv._[1]);
|
||||
deploy.validate_url(url);
|
||||
|
||||
var app_dir = path.resolve(require_project("bundle"));
|
||||
var build_dir = path.join(app_dir, '.skybreak/local/build_tar');
|
||||
var bundle_path = path.join(build_dir, 'bundle');
|
||||
var bundle_opts = { skip_dev_bundle: true };
|
||||
|
||||
var do_deploy = function (password, set_password) {
|
||||
process.stdout.write('Deploying to ' + url.hostname + '. Bundling ... ');
|
||||
|
||||
require('../lib/bundler.js').bundle(app_dir, bundle_path, bundle_opts);
|
||||
|
||||
process.stdout.write('uploading ... ');
|
||||
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
var tar = spawn('tar', ['czf', '-', 'bundle'], {cwd: build_dir});
|
||||
|
||||
var deploy_req_opts = {
|
||||
method: 'POST',
|
||||
host: deploy.HOSTNAME,
|
||||
path: '/deploy/' + url.hostname
|
||||
};
|
||||
var password_opts = {};
|
||||
if (password) password_opts.password = password;
|
||||
if (set_password) password_opts.set_password = set_password;
|
||||
if (password || set_password)
|
||||
deploy_req_opts.path += "?" + require('querystring').stringify(password_opts);
|
||||
|
||||
var http = require('http');
|
||||
var deploy_data = '';
|
||||
var deploy_req = http.request(deploy_req_opts, function (deploy_res) {
|
||||
deploy_res.setEncoding('utf8');
|
||||
deploy_res.on('data', function (chunk) { deploy_data += chunk; });
|
||||
deploy_res.on('end', function () {
|
||||
if (deploy_res.statusCode !== 200) {
|
||||
console.log("failed!");
|
||||
console.log(deploy_data);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.stdout.write('done.\n');
|
||||
process.stdout.write('Now serving at ' + url.hostname + '\n');
|
||||
|
||||
files.rm_recursive(build_dir);
|
||||
|
||||
if (!url.hostname.match('skybreakplatform.com')) {
|
||||
var dns = require('dns');
|
||||
dns.resolve(url.hostname, 'CNAME', function (err, cnames) {
|
||||
if (err || cnames[0] !== 'origin.skybreakplatform.com') {
|
||||
dns.resolve(url.hostname, 'A', function (err, addresses) {
|
||||
if (err || addresses[0] !== '107.22.210.133') {
|
||||
process.stdout.write('-------------\n');
|
||||
process.stdout.write("You've deployed to a custom domain.\n");
|
||||
process.stdout.write("Please be sure to CNAME your hostname to origin.skybreakplatform.com,\n");
|
||||
process.stdout.write("or set an A record to 107.22.210.133.\n");
|
||||
process.stdout.write('-------------\n');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tar.stdout.on('data', function (data) {
|
||||
deploy_req.write(data);
|
||||
});
|
||||
|
||||
tar.on('exit', function (code) {
|
||||
deploy_req.end();
|
||||
});
|
||||
|
||||
// XXX this is gross. maybe some way to automate?
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
};
|
||||
|
||||
deploy.maybe_password(url.hostname, function (password) {
|
||||
if (new_argv.password) {
|
||||
deploy.get_new_password(function (set_password) {
|
||||
do_deploy(password, set_password);
|
||||
});
|
||||
} else {
|
||||
do_deploy(password);
|
||||
}
|
||||
});
|
||||
if (new_argv.delete) {
|
||||
deploy.delete_app(new_argv._[1]);
|
||||
} else {
|
||||
var app_dir = path.resolve(require_project("bundle"));
|
||||
deploy.deploy_app(new_argv._[1], app_dir, new_argv.password);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -612,36 +466,7 @@ Commands.push({
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var deploy = require('./deploy');
|
||||
var url = deploy.parse_url(argv._[0]);
|
||||
deploy.validate_url(url);
|
||||
|
||||
deploy.maybe_password(url.hostname, function (password) {
|
||||
var http = require('http');
|
||||
var options = {
|
||||
host: deploy.HOSTNAME,
|
||||
port: 80,
|
||||
path: '/logs/' + url.hostname,
|
||||
};
|
||||
if (password) {
|
||||
options.path += '?password=' + password;
|
||||
}
|
||||
|
||||
var req = http.get(options, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function (chunk) {
|
||||
process.stdout.write(chunk);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', function(e) {
|
||||
console.log("Error connecting to Skybreak: " + e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// XXX this is gross. maybe some way to automate?
|
||||
process.stdin.destroy(); // clean up after maybe_password
|
||||
});
|
||||
deploy.logs(argv._[0]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user