mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge branch 'legacy-code-cleanup' into devel
This commit is contained in:
@@ -31,8 +31,8 @@ rest of this section will explain the specific API commands in greater detail.
|
||||
// Use Underscore package, but only on the server.
|
||||
// Version not specified, so it will be as of Meteor 0.9.0.
|
||||
api.use('underscore', 'server');
|
||||
// Use application-configuration package, version 1.0.0 or newer.
|
||||
api.use('application-configuration@1.0.0');
|
||||
// Use iron:router package, version 1.0.0 or newer.
|
||||
api.use('iron:router@1.0.0');
|
||||
// Give users of this package access to the Templating package.
|
||||
api.imply('templating')
|
||||
// Export the object 'Email' to packages or apps that use this package.
|
||||
|
||||
1
meteor
1
meteor
@@ -60,7 +60,6 @@ function install_dev_bundle {
|
||||
rm -rf "$BUNDLE_TMPDIR"
|
||||
mkdir "$BUNDLE_TMPDIR"
|
||||
|
||||
# fyi: URL duplicated in packages/dev-bundle-fetcher/dev-bundle
|
||||
DEV_BUNDLE_URL_ROOT="https://d3sqy0vbqsdhku.cloudfront.net/"
|
||||
# If you set $USE_TEST_DEV_BUNDLE_SERVER then we will download
|
||||
# dev bundles copied by copy-dev-bundle-from-jenkins.sh without --prod.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
.build
|
||||
.build*
|
||||
@@ -1,4 +0,0 @@
|
||||
# Application-Configuration
|
||||
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,200 +0,0 @@
|
||||
var Future = Npm.require("fibers/future");
|
||||
|
||||
AppConfig = {};
|
||||
|
||||
|
||||
AppConfig.findGalaxy = _.once(function () {
|
||||
if (!('GALAXY' in process.env || 'ULTRAWORLD_DDP_ENDPOINT' in process.env)) {
|
||||
return null;
|
||||
}
|
||||
return Follower.connect(process.env.ULTRAWORLD_DDP_ENDPOINT || process.env.GALAXY);
|
||||
});
|
||||
|
||||
var ultra = AppConfig.findGalaxy();
|
||||
|
||||
var subFuture = new Future();
|
||||
var subFutureJobs = new Future();
|
||||
if (ultra) {
|
||||
ultra.subscribe("oneApp", process.env.GALAXY_APP, subFuture.resolver());
|
||||
ultra.subscribe("oneJob", process.env.GALAXY_JOB, subFutureJobs.resolver());
|
||||
}
|
||||
|
||||
var Apps;
|
||||
var Jobs;
|
||||
var Services;
|
||||
var collectionFuture = new Future();
|
||||
|
||||
Meteor.startup(function () {
|
||||
var Mongo = Package.mongo.Mongo;
|
||||
if (ultra) {
|
||||
Apps = new Mongo.Collection("apps", {
|
||||
connection: ultra
|
||||
});
|
||||
Jobs = new Mongo.Collection("jobs", {
|
||||
connection: ultra
|
||||
});
|
||||
Services = new Mongo.Collection('services', {
|
||||
connection: ultra
|
||||
});
|
||||
// allow us to block on the collections being ready
|
||||
collectionFuture.return();
|
||||
}
|
||||
});
|
||||
|
||||
// XXX: Remove this once we allow the same collection to be new'd from multiple
|
||||
// places.
|
||||
AppConfig._getAppCollection = function () {
|
||||
collectionFuture.wait();
|
||||
return Apps;
|
||||
};
|
||||
|
||||
AppConfig._getJobsCollection = function () {
|
||||
collectionFuture.wait();
|
||||
return Jobs;
|
||||
};
|
||||
|
||||
|
||||
var staticAppConfig;
|
||||
|
||||
try {
|
||||
if (process.env.APP_CONFIG) {
|
||||
staticAppConfig = JSON.parse(process.env.APP_CONFIG);
|
||||
} else {
|
||||
var settings;
|
||||
try {
|
||||
if (process.env.METEOR_SETTINGS) {
|
||||
settings = JSON.parse(process.env.METEOR_SETTINGS);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.warn("Could not parse METEOR_SETTINGS as JSON");
|
||||
}
|
||||
staticAppConfig = {
|
||||
settings: settings,
|
||||
packages: {
|
||||
'mongo-livedata': {
|
||||
url: process.env.MONGO_URL,
|
||||
oplog: process.env.MONGO_OPLOG_URL
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
Log.warn("Could not parse initial APP_CONFIG environment variable");
|
||||
};
|
||||
|
||||
AppConfig.getAppConfig = function () {
|
||||
if (!subFuture.isResolved() && staticAppConfig) {
|
||||
return staticAppConfig;
|
||||
}
|
||||
subFuture.wait();
|
||||
var myApp = Apps.findOne(process.env.GALAXY_APP);
|
||||
if (!myApp) {
|
||||
throw new Error("there is no app config for this app");
|
||||
}
|
||||
var config = myApp.config;
|
||||
return config;
|
||||
};
|
||||
|
||||
AppConfig.getStarForThisJob = function () {
|
||||
if (ultra) {
|
||||
subFutureJobs.wait();
|
||||
var job = Jobs.findOne(process.env.GALAXY_JOB);
|
||||
if (job) {
|
||||
return job.star;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
AppConfig.configurePackage = function (packageName, configure) {
|
||||
var appConfig = AppConfig.getAppConfig(); // Will either be based in the env var,
|
||||
// or wait for galaxy to connect.
|
||||
var lastConfig =
|
||||
(appConfig && appConfig.packages &&
|
||||
appConfig.packages[packageName]) || {};
|
||||
|
||||
// Always call the configure callback "soon" even if the initial configuration
|
||||
// is empty (synchronously, though deferred would be OK).
|
||||
// XXX make sure that all callers of configurePackage deal well with multiple
|
||||
// callback invocations! eg, email does not
|
||||
configure(lastConfig);
|
||||
var configureIfDifferent = function (app) {
|
||||
if (!EJSON.equals(
|
||||
app.config && app.config.packages && app.config.packages[packageName],
|
||||
lastConfig)) {
|
||||
lastConfig = app.config.packages[packageName];
|
||||
configure(lastConfig);
|
||||
}
|
||||
};
|
||||
var subHandle;
|
||||
var observed = new Future();
|
||||
|
||||
// This is not required to finish, so defer it so it doesn't block anything
|
||||
// else.
|
||||
Meteor.defer( function () {
|
||||
// there's a Meteor.startup() that produces the various collections, make
|
||||
// sure it runs first before we continue.
|
||||
collectionFuture.wait();
|
||||
subHandle = Apps.find(process.env.GALAXY_APP).observe({
|
||||
added: configureIfDifferent,
|
||||
changed: configureIfDifferent
|
||||
});
|
||||
observed.return();
|
||||
});
|
||||
|
||||
return {
|
||||
stop: function () {
|
||||
observed.wait();
|
||||
subHandle.stop();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
AppConfig.configureService = function (serviceName, version, configure) {
|
||||
|
||||
// Collect all the endpoints for this service, from both old- and new-format
|
||||
// documents, and call the `configure` callback with all the service endpoints
|
||||
// that we know about.
|
||||
var callConfigure = function (doc) {
|
||||
var serviceDocs = Services.find({
|
||||
name: serviceName,
|
||||
version: version
|
||||
});
|
||||
var endpoints = [];
|
||||
serviceDocs.forEach(function (serviceDoc) {
|
||||
if (serviceDoc.providers) {
|
||||
_.each(serviceDoc.providers, function (endpoint, app) {
|
||||
endpoints.push(endpoint);
|
||||
});
|
||||
} else {
|
||||
endpoints.push(serviceDoc.endpoint);
|
||||
}
|
||||
});
|
||||
configure(endpoints);
|
||||
};
|
||||
|
||||
if (ultra) {
|
||||
// there's a Meteor.startup() that produces the various collections, make
|
||||
// sure it runs first before we continue.
|
||||
collectionFuture.wait();
|
||||
// First try to subscribe to the new format service registrations; if that
|
||||
// sub doesn't exist, then ultraworld hasn't updated to the new format yet,
|
||||
// so try the old format `servicesByName` sub instead.
|
||||
ultra.subscribe('services', serviceName, version, {
|
||||
onError: function (err) {
|
||||
if (err.error === 404) {
|
||||
ultra.subscribe('servicesByName', serviceName);
|
||||
}
|
||||
}
|
||||
});
|
||||
return Services.find({
|
||||
name: serviceName,
|
||||
version: version
|
||||
}).observe({
|
||||
added: callConfigure,
|
||||
changed: callConfigure,
|
||||
removed: callConfigure
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "Interaction with the configuration sources for your apps",
|
||||
version: '1.0.4'
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['logging', 'underscore', 'ddp', 'ejson', 'follower-livedata']);
|
||||
api.use(['mongo'], {
|
||||
unordered: true
|
||||
});
|
||||
api.addFiles(['config.js'], 'server');
|
||||
api.export('AppConfig', 'server');
|
||||
});
|
||||
1
packages/ctl-helper/.gitignore
vendored
1
packages/ctl-helper/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
1
packages/ctl-helper/.npm/package/.gitignore
vendored
1
packages/ctl-helper/.npm/package/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
@@ -1,7 +0,0 @@
|
||||
This directory and the files immediately inside it are automatically generated
|
||||
when you change this package's NPM dependencies. Commit the files in this
|
||||
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
|
||||
so that others run the same versions of sub-dependencies.
|
||||
|
||||
You should NOT check in the node_modules directory that Meteor automatically
|
||||
creates; if you are using git, the .gitignore file tells git to ignore it.
|
||||
15
packages/ctl-helper/.npm/package/npm-shrinkwrap.json
generated
15
packages/ctl-helper/.npm/package/npm-shrinkwrap.json
generated
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"optimist": {
|
||||
"version": "0.6.0",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,275 +0,0 @@
|
||||
var optimist = Npm.require('optimist');
|
||||
var Future = Npm.require('fibers/future');
|
||||
|
||||
Ctl = {};
|
||||
|
||||
var connection;
|
||||
var checkConnection;
|
||||
|
||||
_.extend(Ctl, {
|
||||
Commands: [],
|
||||
|
||||
main: function (argv) {
|
||||
var opt = optimist(argv)
|
||||
.alias('h', 'help')
|
||||
.boolean('help');
|
||||
argv = opt.argv;
|
||||
|
||||
if (argv.help) {
|
||||
argv._.splice(0, 0, "help");
|
||||
delete argv.help;
|
||||
}
|
||||
|
||||
var cmdName = 'help';
|
||||
if (argv._.length)
|
||||
cmdName = argv._.splice(0,1)[0];
|
||||
|
||||
Ctl.findCommand(cmdName).func(argv);
|
||||
Ctl.disconnect();
|
||||
return 0;
|
||||
},
|
||||
|
||||
startServerlikeProgramIfNotPresent: function (program, tags, admin) {
|
||||
var numServers = Ctl.getJobsByApp(
|
||||
Ctl.myAppName(), {program: program, done: false}).count();
|
||||
if (numServers === 0) {
|
||||
return Ctl.startServerlikeProgram(program, tags, admin);
|
||||
} else {
|
||||
console.log(program, "already running.");
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
startServerlikeProgram: function (program, tags, admin) {
|
||||
var appConfig = Ctl.prettyCall(
|
||||
Ctl.findGalaxy(), 'getAppConfiguration', [Ctl.myAppName()]);
|
||||
if (typeof admin == 'undefined')
|
||||
admin = appConfig.admin;
|
||||
admin = !!admin;
|
||||
|
||||
var jobId = null;
|
||||
var rootUrl = Ctl.rootUrl;
|
||||
if (! rootUrl) {
|
||||
var bindPathPrefix = "";
|
||||
if (admin) {
|
||||
bindPathPrefix = "/" + encodeURIComponent(Ctl.myAppName()).replace(/\./g, '_');
|
||||
}
|
||||
rootUrl = "https://" + appConfig.sitename + bindPathPrefix;
|
||||
}
|
||||
|
||||
// Allow appConfig settings to be objects or strings. We need to stringify
|
||||
// them to pass them to the app in the env var.
|
||||
// Backwards compat with old app config format.
|
||||
_.each(["settings", "METEOR_SETTINGS"], function (settingsKey) {
|
||||
if (appConfig[settingsKey] && typeof appConfig[settingsKey] === "object")
|
||||
appConfig[settingsKey] = JSON.stringify(appConfig[settingsKey]);
|
||||
});
|
||||
|
||||
// XXX args? env?
|
||||
var env = {
|
||||
ROOT_URL: rootUrl,
|
||||
METEOR_SETTINGS: appConfig.settings || appConfig.METEOR_SETTINGS
|
||||
};
|
||||
if (admin)
|
||||
env.ADMIN_APP = 'true';
|
||||
jobId = Ctl.prettyCall(Ctl.findGalaxy(), 'run', [Ctl.myAppName(), program, {
|
||||
exitPolicy: 'restart',
|
||||
env: env,
|
||||
ports: {
|
||||
"main": {
|
||||
bindEnv: "PORT",
|
||||
routeEnv: "ROUTE"//,
|
||||
//bindIpEnv: "BIND_IP" // Later, we can teach Satellite to do
|
||||
//something like recommend the process bind to a particular IP here.
|
||||
//For now, we don't have a way of setting this, so Satellite binds
|
||||
//to 0.0.0.0
|
||||
}
|
||||
},
|
||||
tags: tags
|
||||
}]);
|
||||
console.log("Started", program);
|
||||
return jobId;
|
||||
},
|
||||
|
||||
findCommand: function (name) {
|
||||
var cmd = _.where(Ctl.Commands, { name: name })[0];
|
||||
if (! cmd) {
|
||||
console.log("'" + name + "' is not a ctl command. See 'ctl --help'.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
},
|
||||
|
||||
hasProgram: function (name) {
|
||||
Ctl.subscribeToAppJobs(Ctl.myAppName());
|
||||
var myJob = Ctl.jobsCollection().findOne(Ctl.myJobId());
|
||||
var manifest = Ctl.prettyCall(Ctl.findGalaxy(), 'getStarManifest', [myJob.star]);
|
||||
if (!manifest)
|
||||
return false;
|
||||
var found = false;
|
||||
return _.find(manifest.programs, function (prog) { return prog.name === name; });
|
||||
},
|
||||
|
||||
findGalaxy: _.once(function () {
|
||||
if (!('GALAXY' in process.env)) {
|
||||
console.log(
|
||||
"GALAXY environment variable must be set. See 'galaxy --help'.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
connection = Follower.connect(process.env['ULTRAWORLD_DDP_ENDPOINT']);
|
||||
checkConnection = Meteor.setInterval(function () {
|
||||
if (Ctl.findGalaxy().status().status !== "connected" &&
|
||||
Ctl.findGalaxy().status().retryCount > 2) {
|
||||
console.log("Cannot connect to galaxy; exiting");
|
||||
process.exit(3);
|
||||
}
|
||||
}, 2*1000);
|
||||
return connection;
|
||||
}),
|
||||
|
||||
disconnect: function () {
|
||||
if (connection) {
|
||||
connection.disconnect();
|
||||
}
|
||||
if (checkConnection) {
|
||||
Meteor.clearInterval(checkConnection);
|
||||
checkConnection = null;
|
||||
}
|
||||
},
|
||||
|
||||
updateProxyActiveTags: function (tags, options) {
|
||||
var proxy;
|
||||
var proxyTagSwitchFuture = new Future;
|
||||
options = options || {};
|
||||
AppConfig.configureService('proxy', 'pre0', function (proxyService) {
|
||||
if (proxyService && ! _.isEmpty(proxyService)) {
|
||||
try {
|
||||
proxy = Follower.connect(proxyService, {
|
||||
group: "proxy"
|
||||
});
|
||||
var tries = 0;
|
||||
while (tries < 100) {
|
||||
try {
|
||||
proxy.call('updateTags', Ctl.myAppName(), tags, options);
|
||||
break;
|
||||
} catch (e) {
|
||||
if (e.error === 'not-enough-bindings') {
|
||||
tries++;
|
||||
// try again in a sec.
|
||||
Meteor._sleepForMs(1000);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
proxy.disconnect();
|
||||
if (!proxyTagSwitchFuture.isResolved())
|
||||
proxyTagSwitchFuture['return']();
|
||||
} catch (e) {
|
||||
if (!proxyTagSwitchFuture.isResolved())
|
||||
proxyTagSwitchFuture['throw'](e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var proxyTimeout = Meteor.setTimeout(function () {
|
||||
if (!proxyTagSwitchFuture.isResolved())
|
||||
proxyTagSwitchFuture['throw'](
|
||||
new Error("Timed out looking for a proxy " +
|
||||
"or trying to change tags on it. Status: " +
|
||||
(proxy ? proxy.status().status : "no connection"))
|
||||
);
|
||||
}, 50*1000);
|
||||
proxyTagSwitchFuture.wait();
|
||||
Meteor.clearTimeout(proxyTimeout);
|
||||
},
|
||||
|
||||
jobsCollection: _.once(function () {
|
||||
return new Mongo.Collection("jobs", {manager: Ctl.findGalaxy()});
|
||||
}),
|
||||
|
||||
// use _.memoize so that this is called only once per app.
|
||||
subscribeToAppJobs: _.memoize(function (appName) {
|
||||
Ctl.findGalaxy()._subscribeAndWait("jobsByApp", [appName]);
|
||||
}),
|
||||
|
||||
// XXX this never unsubs...
|
||||
getJobsByApp: function (appName, restOfSelector) {
|
||||
var galaxy = Ctl.findGalaxy();
|
||||
Ctl.subscribeToAppJobs(appName);
|
||||
var selector = {app: appName};
|
||||
if (restOfSelector)
|
||||
_.extend(selector, restOfSelector);
|
||||
return Ctl.jobsCollection().find(selector);
|
||||
},
|
||||
|
||||
myAppName: _.once(function () {
|
||||
if (!('GALAXY_APP' in process.env)) {
|
||||
console.log("GALAXY_APP environment variable must be set.");
|
||||
process.exit(1);
|
||||
}
|
||||
return process.env.GALAXY_APP;
|
||||
}),
|
||||
|
||||
myJobId: _.once(function () {
|
||||
if (!('GALAXY_JOB' in process.env)) {
|
||||
console.log("GALAXY_JOB environment variable must be set.");
|
||||
process.exit(1);
|
||||
}
|
||||
return process.env.GALAXY_JOB;
|
||||
}),
|
||||
|
||||
usage: function() {
|
||||
process.stdout.write(
|
||||
"Usage: ctl [--help] <command> [<args>]\n" +
|
||||
"\n" +
|
||||
"For now, the GALAXY environment variable must be set to the location of\n" +
|
||||
"your Galaxy management server (Ultraworld.) This string is in the same\n" +
|
||||
"format as the argument to DDP.connect().\n" +
|
||||
"\n" +
|
||||
"Commands:\n");
|
||||
_.each(Ctl.Commands, function (cmd) {
|
||||
if (cmd.help && ! cmd.hidden) {
|
||||
var name = cmd.name + " ".substr(cmd.name.length);
|
||||
process.stdout.write(" " + name + cmd.help + "\n");
|
||||
}
|
||||
});
|
||||
process.stdout.write("\n");
|
||||
process.stdout.write(
|
||||
"See 'ctl help <command>' for details on a command.\n");
|
||||
process.exit(1);
|
||||
},
|
||||
|
||||
// XXX copied to meteor/tools/deploy-galaxy.js
|
||||
exitWithError: function (error, messages) {
|
||||
messages = messages || {};
|
||||
|
||||
if (! (error instanceof Meteor.Error))
|
||||
throw error; // get a stack
|
||||
|
||||
var msg = messages[error.error];
|
||||
if (msg)
|
||||
process.stderr.write(msg + "\n");
|
||||
else if (error instanceof Meteor.Error)
|
||||
process.stderr.write("Denied: " + error.message + "\n");
|
||||
|
||||
process.exit(1);
|
||||
},
|
||||
|
||||
// XXX copied to meteor/tools/deploy-galaxy.js
|
||||
prettyCall: function (galaxy, name, args, messages) {
|
||||
try {
|
||||
var ret = galaxy.apply(name, args);
|
||||
} catch (e) {
|
||||
Ctl.exitWithError(e, messages);
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
kill: function (programName, jobId) {
|
||||
console.log("Killing %s (%s)", programName, jobId);
|
||||
Ctl.prettyCall(Ctl.findGalaxy(), 'kill', [jobId]);
|
||||
}
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "Helpers for control programs",
|
||||
version: "1.0.5"
|
||||
});
|
||||
|
||||
Npm.depends({optimist: '0.6.0'});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['logging', 'underscore', 'ddp', 'mongo', 'follower-livedata', 'application-configuration'], 'server');
|
||||
api.export('Ctl', 'server');
|
||||
api.addFiles('ctl-helper.js', 'server');
|
||||
});
|
||||
1
packages/ctl/.gitignore
vendored
1
packages/ctl/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -1,2 +0,0 @@
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,194 +0,0 @@
|
||||
var Future = Npm.require("fibers/future");
|
||||
|
||||
Ctl.Commands.push({
|
||||
name: "help",
|
||||
func: function (argv) {
|
||||
if (!argv._.length || argv.help)
|
||||
Ctl.usage();
|
||||
var cmd = argv._.splice(0,1)[0];
|
||||
argv.help = true;
|
||||
|
||||
Ctl.findCommand(cmd).func(argv);
|
||||
}
|
||||
});
|
||||
|
||||
var startFun = function (argv) {
|
||||
if (argv.help || argv._.length !== 0) {
|
||||
process.stderr.write(
|
||||
"Usage: ctl start\n" +
|
||||
"\n" +
|
||||
"Starts the app. For now, this just means that it runs the 'server'\n" +
|
||||
"program.\n"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
Ctl.subscribeToAppJobs(Ctl.myAppName());
|
||||
var jobs = Ctl.jobsCollection();
|
||||
var thisJob = jobs.findOne(Ctl.myJobId());
|
||||
Ctl.updateProxyActiveTags(['', thisJob.star]);
|
||||
if (Ctl.hasProgram("console")) {
|
||||
console.log("starting console for app", Ctl.myAppName());
|
||||
Ctl.startServerlikeProgramIfNotPresent("console", ["admin"], true);
|
||||
}
|
||||
console.log("starting server for app", Ctl.myAppName());
|
||||
Ctl.startServerlikeProgramIfNotPresent("server", ["runner"]);
|
||||
};
|
||||
|
||||
Ctl.Commands.push({
|
||||
name: "start",
|
||||
help: "Start this app",
|
||||
func: startFun
|
||||
});
|
||||
|
||||
|
||||
Ctl.Commands.push({
|
||||
name: "endUpdate",
|
||||
help: "Start this app to end an update",
|
||||
func: startFun
|
||||
});
|
||||
|
||||
var stopFun = function (argv) {
|
||||
if (argv.help || argv._.length !== 0) {
|
||||
process.stderr.write(
|
||||
"Usage: ctl stop\n" +
|
||||
"\n" +
|
||||
"Stops the app. For now, this just means that it kills all jobs\n" +
|
||||
"other than itself.\n"
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get all jobs (other than this job: don't commit suicide!) that are not
|
||||
// already killed.
|
||||
var jobs = Ctl.getJobsByApp(
|
||||
Ctl.myAppName(), {_id: {$ne: Ctl.myJobId()}, done: false});
|
||||
jobs.forEach(function (job) {
|
||||
// Don't commit suicide.
|
||||
if (job._id === Ctl.myJobId())
|
||||
return;
|
||||
// It's dead, Jim.
|
||||
if (job.done)
|
||||
return;
|
||||
Ctl.kill(job.program, job._id);
|
||||
});
|
||||
console.log("Server stopped.");
|
||||
};
|
||||
|
||||
Ctl.Commands.push({
|
||||
name: "stop",
|
||||
help: "Stop this app",
|
||||
func: stopFun
|
||||
});
|
||||
|
||||
var waitForDone = function (jobCollection, jobId) {
|
||||
var fut = new Future();
|
||||
var found = false;
|
||||
try {
|
||||
var observation = jobCollection.find(jobId).observe({
|
||||
added: function (doc) {
|
||||
found = true;
|
||||
if (doc.done)
|
||||
fut['return']();
|
||||
},
|
||||
changed: function (doc) {
|
||||
if (doc.done)
|
||||
fut['return']();
|
||||
},
|
||||
removed: function (doc) {
|
||||
fut['return']();
|
||||
}
|
||||
});
|
||||
// if the document doesn't exist at all, it's certainly done.
|
||||
if (!found)
|
||||
fut['return']();
|
||||
fut.wait();
|
||||
} finally {
|
||||
observation.stop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Ctl.Commands.push({
|
||||
name: "beginUpdate",
|
||||
help: "Stop this app to begin an update",
|
||||
func: function (argv) {
|
||||
Ctl.subscribeToAppJobs(Ctl.myAppName());
|
||||
var jobs = Ctl.jobsCollection();
|
||||
var thisJob = jobs.findOne(Ctl.myJobId());
|
||||
// Look at all the server jobs that are on the old star.
|
||||
var oldJobSelector = {
|
||||
app: Ctl.myAppName(),
|
||||
star: {$ne: thisJob.star},
|
||||
program: "server",
|
||||
done: false
|
||||
};
|
||||
var oldServers = jobs.find(oldJobSelector).fetch();
|
||||
// Start a new job for each of them.
|
||||
var newServersAlreadyPresent = jobs.find({
|
||||
app: Ctl.myAppName(),
|
||||
star: thisJob.star,
|
||||
program: "server",
|
||||
done: false
|
||||
}).count();
|
||||
// discount any new servers we've already started.
|
||||
oldServers.splice(0, newServersAlreadyPresent);
|
||||
console.log("starting " + oldServers.length + " new servers to match old");
|
||||
_.each(oldServers, function (oldServer) {
|
||||
Ctl.startServerlikeProgram("server",
|
||||
oldServer.tags,
|
||||
oldServer.env.ADMIN_APP);
|
||||
});
|
||||
// Wait for them all to come up and bind to the proxy.
|
||||
var updateProxyActiveTagsOptions = {
|
||||
requireRegisteredBindingCount: {}
|
||||
};
|
||||
// How many new servers should be up when we update the tags, given how many
|
||||
// servers we're aiming at:
|
||||
var target;
|
||||
switch (oldServers.length) {
|
||||
case 0:
|
||||
target = 0;
|
||||
break;
|
||||
case 1:
|
||||
target = 1;
|
||||
break;
|
||||
case 2:
|
||||
target = 1;
|
||||
break;
|
||||
default:
|
||||
var c = oldServers.length;
|
||||
target = Math.min(c - 1, Math.ceil(c*.8));
|
||||
break;
|
||||
}
|
||||
updateProxyActiveTagsOptions.requireRegisteredBindingCount[thisJob.star] =
|
||||
target;
|
||||
Ctl.updateProxyActiveTags(['', thisJob.star], updateProxyActiveTagsOptions);
|
||||
|
||||
// (eventually) tell the proxy to switch over to using the new star
|
||||
// One by one, kill all the old star's server jobs.
|
||||
var jobToKill = jobs.findOne(oldJobSelector);
|
||||
while (jobToKill) {
|
||||
Ctl.kill("server", jobToKill._id);
|
||||
// Wait for it to go down
|
||||
waitForDone(jobs, jobToKill._id);
|
||||
// Spend some time in between to allow any reconnect storm to die down.
|
||||
Meteor._sleepForMs(5000);
|
||||
jobToKill = jobs.findOne(oldJobSelector);
|
||||
}
|
||||
// Now kill all old non-server jobs. They're less important.
|
||||
jobs.find({
|
||||
app: Ctl.myAppName(),
|
||||
star: {$ne: thisJob.star},
|
||||
program: {$ne: "server"},
|
||||
done: false
|
||||
}).forEach(function (job) {
|
||||
Ctl.kill(job.program, job._id);
|
||||
});
|
||||
// fin
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
main = function (argv) {
|
||||
return Ctl.main(argv);
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "Default control program for an application",
|
||||
version: "1.0.3"
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['underscore', 'ddp', 'mongo', 'ctl-helper', 'application-configuration', 'follower-livedata'], 'server');
|
||||
api.export('main', 'server');
|
||||
api.addFiles('ctl.js', 'server');
|
||||
});
|
||||
@@ -56,28 +56,10 @@ StreamServer = function () {
|
||||
self.server.installHandlers(Package.webapp.WebApp.httpServer);
|
||||
Package.webapp.WebApp.httpServer.addListener('request', Package.webapp.WebApp._timeoutAdjustmentRequestCallback);
|
||||
|
||||
Package.webapp.WebApp.httpServer.on('meteor-closing', function () {
|
||||
_.each(self.open_sockets, function (socket) {
|
||||
socket.end();
|
||||
});
|
||||
});
|
||||
|
||||
// Support the /websocket endpoint
|
||||
self._redirectWebsocketEndpoint();
|
||||
|
||||
self.server.on('connection', function (socket) {
|
||||
|
||||
if (Package.webapp.WebAppInternals.usingDdpProxy) {
|
||||
// If we are behind a DDP proxy, immediately close any sockjs connections
|
||||
// that are not using websockets; the proxy will terminate sockjs for us,
|
||||
// so we don't expect to be handling any other transports.
|
||||
if (socket.protocol !== "websocket" &&
|
||||
socket.protocol !== "websocket-raw") {
|
||||
socket.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
socket.send = function (data) {
|
||||
socket.write(data);
|
||||
};
|
||||
|
||||
1
packages/dev-bundle-fetcher/.gitignore
vendored
1
packages/dev-bundle-fetcher/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -1,2 +0,0 @@
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,78 +0,0 @@
|
||||
set -e
|
||||
trap "echo Failed to fetch binary dependencies." EXIT
|
||||
|
||||
if [ -z "$DATA_DIR" ]; then
|
||||
echo "Please set DATA_DIR to a writeable directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
# Duplicated from scripts/admin/launch-meteor, except that we hardcode the path
|
||||
# to sysctl. (When satellite spawns ultraworld, ultraworld doesn't have a PATH
|
||||
# and so can't find sysctl itself.)
|
||||
UNAME=$(uname)
|
||||
if [ "$UNAME" != "Linux" -a "$UNAME" != "Darwin" ] ; then
|
||||
echo "Sorry, this OS is not supported yet." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If you change this, also change host() in tools/archinfo.js
|
||||
if [ "$UNAME" = "Darwin" ] ; then
|
||||
if [ "i386" != "$(uname -p)" -o "1" != "$(/usr/sbin/sysctl -n hw.cpu64bit_capable 2>/dev/null || echo 0)" ] ; then
|
||||
# Can't just test uname -m = x86_64, because Snow Leopard can
|
||||
# return other values.
|
||||
echo "Only 64-bit Intel processors are supported at this time." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
ARCH="x86_64"
|
||||
elif [ "$UNAME" = "Linux" ] ; then
|
||||
ARCH="$(uname -m)"
|
||||
if [ "$ARCH" != "i686" -a "$ARCH" != "x86_64" ] ; then
|
||||
echo "Unsupported architecture: $ARCH" 1>&2
|
||||
echo "Meteor only supports i686 and x86_64 for now." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
PLATFORM="${UNAME}_${ARCH}"
|
||||
|
||||
|
||||
# XXX don't hardcode linux :)
|
||||
TARBALL="dev_bundle_${PLATFORM}_##BUNDLE_VERSION##.tar.gz"
|
||||
BUNDLE_TMPDIR="$DATA_DIR/dependencies.fetch"
|
||||
|
||||
rm -rf "$BUNDLE_TMPDIR"
|
||||
mkdir "$BUNDLE_TMPDIR"
|
||||
|
||||
# Cache dev bundles in /tmp.
|
||||
# XXX something more secure is needed in production
|
||||
CACHED_TARBALL="/tmp/$TARBALL"
|
||||
if [ ! -r "$CACHED_TARBALL" ]; then
|
||||
# Duplicated from 'meteor' script in root of repository
|
||||
TEMP_TARBALL="${CACHED_TARBALL}.tmp.${RANDOM}"
|
||||
curl -s "https://d3sqy0vbqsdhku.cloudfront.net/$TARBALL" >"$TEMP_TARBALL"
|
||||
mv "$TEMP_TARBALL" "$CACHED_TARBALL"
|
||||
fi
|
||||
tar -xzf "$CACHED_TARBALL" -C "$BUNDLE_TMPDIR"
|
||||
|
||||
# Delete old dev bundle and rename the new one on top of it.
|
||||
# XXX probably we can just trust that dependencies from last time are good
|
||||
DEPS_DIR="$DATA_DIR/dependencies"
|
||||
rm -rf "$DEPS_DIR"
|
||||
mv "$BUNDLE_TMPDIR" "$DEPS_DIR"
|
||||
|
||||
trap - EXIT
|
||||
set +e
|
||||
|
||||
# If there are global node_modules in the bundle, remove them, since
|
||||
# they override NODE_PATH. Why are these present? Well, 'meteor
|
||||
# bundle' historically embeds them to give you a self-contained
|
||||
# bundle. But that never worked very well, because you'd get the
|
||||
# version for the arch you built on, and you'd have to manually
|
||||
# rebuild the binary modules (node-fibers) on the target system. It is
|
||||
# not ideal to have the bundle modify itself (much better for it to be
|
||||
# immutable) but it'll do for now.
|
||||
rm -rf node_modules 2>/dev/null || true
|
||||
|
||||
export NODE_PATH="$DEPS_DIR/lib/node_modules"
|
||||
exec "$DEPS_DIR/bin/node" ##RUN_FILE## ##IMAGE## "$@"
|
||||
@@ -1,5 +0,0 @@
|
||||
DevBundleFetcher = {
|
||||
script: function () {
|
||||
return Assets.getText("dev-bundle");
|
||||
}
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "A shell script for downloading the Meteor dev bundle",
|
||||
version: "1.0.2"
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.export('DevBundleFetcher', 'server');
|
||||
api.addFiles(['dev-bundle', 'dev-bundle.js'], ['server']);
|
||||
});
|
||||
@@ -31,29 +31,14 @@ var makePool = function (mailUrlString) {
|
||||
return pool;
|
||||
};
|
||||
|
||||
// We construct smtpPool at the first call to Email.send, so that
|
||||
// Meteor.startup code can set $MAIL_URL.
|
||||
var smtpPoolFuture = new Future;;
|
||||
var configured = false;
|
||||
|
||||
var getPool = function () {
|
||||
// We check MAIL_URL in case someone else set it in Meteor.startup code.
|
||||
if (!configured) {
|
||||
configured = true;
|
||||
AppConfig.configurePackage('email', function (config) {
|
||||
// XXX allow reconfiguration when the app config changes
|
||||
if (smtpPoolFuture.isResolved())
|
||||
return;
|
||||
var url = config.url || process.env.MAIL_URL;
|
||||
var pool = null;
|
||||
if (url)
|
||||
pool = makePool(url);
|
||||
smtpPoolFuture.return(pool);
|
||||
});
|
||||
}
|
||||
|
||||
return smtpPoolFuture.wait();
|
||||
};
|
||||
var getPool = _.once(function () {
|
||||
// We delay this check until the first call to Email.send, in case someone
|
||||
// set process.env.MAIL_URL in startup code.
|
||||
var url = process.env.MAIL_URL;
|
||||
if (! url)
|
||||
return null;
|
||||
return makePool(url);
|
||||
});
|
||||
|
||||
var next_devmode_mail_id = 0;
|
||||
var output_stream = process.stdout;
|
||||
|
||||
@@ -12,7 +12,6 @@ Npm.depends({
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use('underscore', 'server');
|
||||
api.use('application-configuration');
|
||||
api.export('Email', 'server');
|
||||
api.export('EmailTest', 'server', {testOnly: true});
|
||||
api.addFiles('email.js', 'server');
|
||||
|
||||
1
packages/follower-livedata/.gitignore
vendored
1
packages/follower-livedata/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -1,2 +0,0 @@
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,244 +0,0 @@
|
||||
var fs = Npm.require('fs');
|
||||
var Future = Npm.require('fibers/future');
|
||||
|
||||
|
||||
var MONITOR_INTERVAL = 5*1000; // every 5 seconds
|
||||
|
||||
/**
|
||||
* Follower.connect() replaces DDP.connect() for connecting to DDP services that
|
||||
* implement a leadership set. The follower connection tries to keep connected
|
||||
* to the leader, and fails over as the leader changes.
|
||||
*
|
||||
* Options: {
|
||||
* group: The name of the leadership group to connect to. Default "package.leadershipLivedata"
|
||||
* }
|
||||
*
|
||||
* A Follower connection implements the following interfaces over and above a
|
||||
* normal DDP connection:
|
||||
*
|
||||
* onLost(callback): calls callback when the library considers itself to have
|
||||
* tried all its known options for the leadership group.
|
||||
*
|
||||
* onFound(callback): Called when the follower was previously lost, but has now
|
||||
* successfully connected to something in the right leadership group.
|
||||
*/
|
||||
Follower = {
|
||||
connect: function (urlSet, options) {
|
||||
var electorTries;
|
||||
options = _.extend({
|
||||
group: "package.leadershipLivedata"
|
||||
}, options);
|
||||
// start each elector as untried/assumed connectable.
|
||||
|
||||
var makeElectorTries = function (urlSet) {
|
||||
|
||||
electorTries = {};
|
||||
if (typeof urlSet === 'string') {
|
||||
urlSet = _.map(urlSet.split(','), function (url) {return url.trim();});
|
||||
}
|
||||
_.each(urlSet, function (url) {
|
||||
electorTries[url] = 0;
|
||||
});
|
||||
};
|
||||
|
||||
makeElectorTries(urlSet);
|
||||
|
||||
var tryingUrl = null;
|
||||
var outstandingGetElectorate = false;
|
||||
var conn = null;
|
||||
var prevReconnect = null;
|
||||
var prevDisconnect = null;
|
||||
var prevApply = null;
|
||||
var leader = null;
|
||||
var connectedTo = null;
|
||||
var intervalHandle = null;
|
||||
|
||||
|
||||
// Used to defer all method calls until we're sure that we connected to the
|
||||
// right leadership group.
|
||||
var connectedToLeadershipGroup = new Future();
|
||||
|
||||
var lost = false;
|
||||
var lostCallbacks = [];
|
||||
var foundCallbacks = [];
|
||||
|
||||
var findFewestTries = function () {
|
||||
var min = 10000;
|
||||
var minElector = null;
|
||||
_.each(electorTries, function (tries, elector) {
|
||||
if (tries < min) {
|
||||
min = tries;
|
||||
minElector = elector;
|
||||
}
|
||||
});
|
||||
if (min > 1 && !lost) {
|
||||
// we've tried everything once; we just became lost.
|
||||
lost = true;
|
||||
_.each(lostCallbacks, function (f) { f(); });
|
||||
}
|
||||
return minElector;
|
||||
};
|
||||
|
||||
var updateElectorate = function (res) {
|
||||
leader = res.leader;
|
||||
electorTries = {};
|
||||
_.each(res.electorate, function (elector) {
|
||||
electorTries[elector] = 0; // verified that this is in the current elector set.
|
||||
});
|
||||
};
|
||||
|
||||
var tryElector = function (url) {
|
||||
if (tryingUrl) {
|
||||
electorTries[tryingUrl]++;
|
||||
}
|
||||
url = url || findFewestTries();
|
||||
//console.log("trying", url, electorTries, tryingUrl, process.env.GALAXY_JOB);
|
||||
|
||||
// Don't keep trying the same url as fast as we can if it's not working.
|
||||
if (electorTries[url] > 2) {
|
||||
Meteor._sleepForMs(3 * 1000);
|
||||
}
|
||||
|
||||
if (conn) {
|
||||
prevReconnect.apply(conn, [{
|
||||
url: url
|
||||
}]);
|
||||
} else {
|
||||
conn = DDP.connect(url, options);
|
||||
prevReconnect = conn.reconnect;
|
||||
prevDisconnect = conn.disconnect;
|
||||
prevApply = conn.apply;
|
||||
}
|
||||
tryingUrl = url;
|
||||
|
||||
if (!outstandingGetElectorate) {
|
||||
outstandingGetElectorate = true;
|
||||
conn.call('getElectorate', options.group, function (err, res) {
|
||||
outstandingGetElectorate = false;
|
||||
connectedTo = tryingUrl;
|
||||
if (err) {
|
||||
tryElector();
|
||||
return;
|
||||
}
|
||||
if (!_.contains(res.electorate, connectedTo)) {
|
||||
Log.warn("electorate " + res.electorate + " does not contain " + connectedTo);
|
||||
}
|
||||
tryingUrl = null;
|
||||
if (! connectedToLeadershipGroup.isResolved()) {
|
||||
connectedToLeadershipGroup["return"]();
|
||||
}
|
||||
// we got an answer! Connected!
|
||||
electorTries[url] = 0;
|
||||
|
||||
if (res.leader === connectedTo) {
|
||||
// we're good.
|
||||
if (lost) {
|
||||
// we're found.
|
||||
lost = false;
|
||||
_.each(foundCallbacks, function (f) { f(); });
|
||||
}
|
||||
} else {
|
||||
// let's connect to the leader anyway, if we think it
|
||||
// is connectable.
|
||||
if (electorTries[res.leader] == 0) {
|
||||
tryElector(res.leader);
|
||||
} else {
|
||||
// XXX: leader is probably down, we're probably going to elect
|
||||
// soon. Wait for the next round.
|
||||
}
|
||||
|
||||
}
|
||||
updateElectorate(res);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
tryElector();
|
||||
|
||||
var checkConnection = function () {
|
||||
if (conn.status().status !== 'connected' || connectedTo !== leader) {
|
||||
tryElector();
|
||||
} else {
|
||||
conn.call('getElectorate', options.group, function (err, res) {
|
||||
if (err) {
|
||||
electorTries[connectedTo]++;
|
||||
tryElector();
|
||||
} else if (res.leader !== leader) {
|
||||
// update the electorate, and then definitely try to connect to the leader.
|
||||
updateElectorate(res);
|
||||
tryElector(res.leader);
|
||||
} else {
|
||||
if (! connectedToLeadershipGroup.isResolved()) {
|
||||
connectedToLeadershipGroup["return"]();
|
||||
}
|
||||
//console.log("updating electorate with", res);
|
||||
updateElectorate(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var monitorConnection = function () {
|
||||
return Meteor.setInterval(checkConnection, MONITOR_INTERVAL);
|
||||
};
|
||||
|
||||
intervalHandle = monitorConnection();
|
||||
|
||||
conn.disconnect = function () {
|
||||
if (intervalHandle)
|
||||
Meteor.clearInterval(intervalHandle);
|
||||
intervalHandle = null;
|
||||
prevDisconnect.apply(conn);
|
||||
};
|
||||
|
||||
conn.reconnect = function () {
|
||||
if (!intervalHandle)
|
||||
intervalHandle = monitorConnection();
|
||||
if (arguments[0] && arguments[0].url) {
|
||||
makeElectorTries(arguments[0].url);
|
||||
tryElector();
|
||||
} else {
|
||||
prevReconnect.apply(conn, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
conn.getUrl = function () {
|
||||
return _.keys(electorTries).join(',');
|
||||
};
|
||||
|
||||
conn.tries = function () {
|
||||
return electorTries;
|
||||
};
|
||||
|
||||
|
||||
// Assumes that `call` is implemented in terms of `apply`. All method calls
|
||||
// should be deferred until we are sure we've connected to the right
|
||||
// leadership group.
|
||||
conn.apply = function (/* arguments */) {
|
||||
var args = _.toArray(arguments);
|
||||
if (typeof args[args.length-1] === 'function') {
|
||||
// this needs to be independent of this fiber if there is a callback.
|
||||
Meteor.defer(function () {
|
||||
connectedToLeadershipGroup.wait();
|
||||
return prevApply.apply(conn, args);
|
||||
});
|
||||
return null; // if there is a callback, the return value is not used
|
||||
} else {
|
||||
connectedToLeadershipGroup.wait();
|
||||
return prevApply.apply(conn, args);
|
||||
}
|
||||
};
|
||||
|
||||
conn.onLost = function (callback) {
|
||||
lostCallbacks.push(callback);
|
||||
};
|
||||
|
||||
conn.onFound = function (callback) {
|
||||
foundCallbacks.push(callback);
|
||||
};
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "Maintain a connection to the leader of an election set",
|
||||
version: '1.0.3'
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['logging', 'underscore', 'ddp', 'ejson']);
|
||||
api.addFiles(['follower.js'], 'server');
|
||||
api.export('Follower');
|
||||
});
|
||||
@@ -5,26 +5,7 @@ Meteor = {
|
||||
|
||||
Meteor.settings = {};
|
||||
|
||||
if (process.env.APP_CONFIG) {
|
||||
// put settings from the app configuration in the settings. Don't depend on
|
||||
// the Galaxy package for now, to avoid silly loops.
|
||||
try {
|
||||
var appConfig = JSON.parse(process.env.APP_CONFIG);
|
||||
if (!appConfig.settings) {
|
||||
Meteor.settings = {};
|
||||
} else if (typeof appConfig.settings === "string") {
|
||||
Meteor.settings = JSON.parse(appConfig.settings);
|
||||
} else {
|
||||
// Old versions of Galaxy may store settings in MongoDB as objects. Newer
|
||||
// versions store it as strings (so that we aren't restricted to
|
||||
// MongoDB-compatible objects). This line makes it work on older Galaxies.
|
||||
// XXX delete this eventually
|
||||
Meteor.settings = appConfig.settings;
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error("Settings from APP_CONFIG are not valid JSON: " + process.env.APP_CONFIG);
|
||||
}
|
||||
} else if (process.env.METEOR_SETTINGS) {
|
||||
if (process.env.METEOR_SETTINGS) {
|
||||
try {
|
||||
Meteor.settings = JSON.parse(process.env.METEOR_SETTINGS);
|
||||
} catch (e) {
|
||||
|
||||
@@ -1229,7 +1229,7 @@ MongoConnection.prototype._observeChangesTailable = function (
|
||||
|
||||
// XXX We probably need to find a better way to expose this. Right now
|
||||
// it's only used by tests, but in fact you need it in normal
|
||||
// operation to interact with capped collections (eg, Galaxy uses it).
|
||||
// operation to interact with capped collections.
|
||||
MongoInternals.MongoTimestamp = MongoDB.Timestamp;
|
||||
|
||||
MongoInternals.Connection = MongoConnection;
|
||||
|
||||
@@ -22,7 +22,7 @@ Npm.strip({
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['random', 'ejson', 'json', 'underscore', 'minimongo', 'logging',
|
||||
'ddp', 'tracker', 'application-configuration'],
|
||||
'ddp', 'tracker'],
|
||||
['client', 'server']);
|
||||
api.use('check', ['client', 'server']);
|
||||
|
||||
|
||||
@@ -24,22 +24,16 @@ _.extend(MongoInternals.RemoteCollectionDriver.prototype, {
|
||||
// only require Mongo configuration if it's actually used (eg, not if
|
||||
// you're only trying to receive data from a remote DDP server.)
|
||||
MongoInternals.defaultRemoteCollectionDriver = _.once(function () {
|
||||
var mongoUrl;
|
||||
var connectionOptions = {};
|
||||
|
||||
AppConfig.configurePackage("mongo-livedata", function (config) {
|
||||
// This will keep running if mongo gets reconfigured. That's not ideal, but
|
||||
// should be ok for now.
|
||||
mongoUrl = config.url;
|
||||
var mongoUrl = process.env.MONGO_URL;
|
||||
|
||||
if (config.oplog)
|
||||
connectionOptions.oplogUrl = config.oplog;
|
||||
});
|
||||
if (process.env.MONGO_OPLOG_URL) {
|
||||
connectionOptions.oplogUrl = process.env.MONGO_OPLOG_URL;
|
||||
}
|
||||
|
||||
// XXX bad error since it could also be set directly in METEOR_DEPLOY_CONFIG
|
||||
if (! mongoUrl)
|
||||
throw new Error("MONGO_URL must be set in environment");
|
||||
|
||||
|
||||
return new MongoInternals.RemoteCollectionDriver(mongoUrl, connectionOptions);
|
||||
});
|
||||
|
||||
1
packages/star-translate/.gitignore
vendored
1
packages/star-translate/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
.build*
|
||||
@@ -1 +0,0 @@
|
||||
node_modules
|
||||
@@ -1,7 +0,0 @@
|
||||
This directory and the files immediately inside it are automatically generated
|
||||
when you change this package's NPM dependencies. Commit the files in this
|
||||
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
|
||||
so that others run the same versions of sub-dependencies.
|
||||
|
||||
You should NOT check in the node_modules directory that Meteor automatically
|
||||
creates; if you are using git, the .gitignore file tells git to ignore it.
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"ncp": {
|
||||
"version": "0.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
This was an internal Meteor package used for interacting with internal prototype
|
||||
systems that no longer exist.
|
||||
@@ -1,12 +0,0 @@
|
||||
Package.describe({
|
||||
summary: "A package for translating old bundles into stars",
|
||||
version: "1.0.5"
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['dev-bundle-fetcher']);
|
||||
api.export('StarTranslator');
|
||||
api.addFiles(['translator.js'], 'server');
|
||||
});
|
||||
|
||||
Npm.depends({ncp: "0.4.2"});
|
||||
@@ -1,112 +0,0 @@
|
||||
var fs = Npm.require('fs');
|
||||
var path = Npm.require('path');
|
||||
var ncp = Npm.require('ncp').ncp;
|
||||
|
||||
StarTranslator = {};
|
||||
|
||||
// Produces a star version of bundlePath in translatedPath, where bundlePath can
|
||||
// point to either an old Meteor bundle or a star. Returns the star's manifest.
|
||||
// bundlePath can equal translatedPath, in which case bundlePath is converted
|
||||
// directly into a star.
|
||||
StarTranslator.maybeTranslate = function (bundlePath, translatedPath) {
|
||||
var self = this;
|
||||
if (path.resolve(bundlePath) !== path.resolve(translatedPath)) {
|
||||
var _ncp = Meteor.wrapAsync(ncp);
|
||||
_ncp(bundlePath, translatedPath);
|
||||
}
|
||||
|
||||
try {
|
||||
// If the directory contains a star.json file with JSON inside it, then we
|
||||
// consider it a star. Otherwise we translate it into a star.
|
||||
var manifest = JSON.parse(fs.readFileSync(path.join(translatedPath,
|
||||
"star.json"),
|
||||
'utf8'));
|
||||
return manifest;
|
||||
} catch (e) {
|
||||
return self._translate(translatedPath);
|
||||
}
|
||||
};
|
||||
|
||||
StarTranslator._translate = function (bundlePath) {
|
||||
var self = this;
|
||||
var clientProgPath = path.join(bundlePath, 'client.json');
|
||||
var serverProgPath = path.join(bundlePath, 'server.sh');
|
||||
var starPath = path.join(bundlePath, 'star.json');
|
||||
|
||||
// Format defined in meteor/tools/bundler.js
|
||||
var manifest = {
|
||||
"format": "site-archive-pre1",
|
||||
"builtBy": "Star translator",
|
||||
"programs": [
|
||||
{
|
||||
"name": "web.browser",
|
||||
"arch": "web.browser",
|
||||
"path": "client.json"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"arch": self._getArch(),
|
||||
"path": "server.sh"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
self._writeServerProg(bundlePath, serverProgPath);
|
||||
self._writeClientProg(bundlePath, clientProgPath);
|
||||
|
||||
fs.writeFileSync(starPath, JSON.stringify(manifest, null, 2));
|
||||
return manifest;
|
||||
};
|
||||
|
||||
StarTranslator._writeServerProg = function (bundlePath, serverProgPath) {
|
||||
var platform = this._getPlatform();
|
||||
var bundleVersion = this._getBundleVersion(bundlePath);
|
||||
var runFile = 'main.js';
|
||||
var serverScript = DevBundleFetcher.script();
|
||||
// Duplicated from meteor/tools/bundler.js
|
||||
serverScript = serverScript.replace(/##PLATFORM##/g, platform);
|
||||
serverScript = serverScript.replace(/##BUNDLE_VERSION##/g, bundleVersion);
|
||||
serverScript = serverScript.replace(/##RUN_FILE##/g, runFile);
|
||||
serverScript = serverScript.replace(/##IMAGE##/g, '');
|
||||
fs.writeFileSync(serverProgPath, serverScript);
|
||||
fs.chmodSync(serverProgPath, '744');
|
||||
};
|
||||
|
||||
StarTranslator._getArch = function () {
|
||||
return Meteor.settings.arch;
|
||||
};
|
||||
|
||||
StarTranslator._getPlatform = function () {
|
||||
var self = this;
|
||||
// Duplicated from meteor/tools/bundler.js
|
||||
var archToPlatform = {
|
||||
'os.linux.x86_32': 'Linux_i686',
|
||||
'os.linux.x86_64': 'Linux_x86_64',
|
||||
'os.osx.x86_64': 'Darwin_x86_64'
|
||||
};
|
||||
return archToPlatform[self._getArch()];
|
||||
};
|
||||
|
||||
StarTranslator._getBundleVersion = function (bundlePath) {
|
||||
var version = fs.readFileSync(path.join(bundlePath,
|
||||
"server", ".bundle_version.txt"),
|
||||
'utf8');
|
||||
return version.trim();
|
||||
};
|
||||
|
||||
StarTranslator._writeClientProg = function (bundlePath, clientProgPath) {
|
||||
var origClientManifest = JSON.parse(fs.readFileSync(path.join(bundlePath,
|
||||
"app.json"),
|
||||
'utf8'));
|
||||
var clientManifest = {
|
||||
"format": "web-program-pre1",
|
||||
"manifest": origClientManifest.manifest,
|
||||
// XXX Haven't updated this for the app.html -> head/body change, but
|
||||
// surely we don't need to because code in pre-star apps doesn't
|
||||
// even read this file?
|
||||
"page": "app.html",
|
||||
"static": "static",
|
||||
"staticCacheable": "static_cacheable"
|
||||
};
|
||||
fs.writeFileSync(clientProgPath, JSON.stringify(clientManifest, null, 2));
|
||||
};
|
||||
@@ -16,9 +16,7 @@ Package.onUse(function (api) {
|
||||
api.use(['logging', 'underscore', 'routepolicy', 'boilerplate-generator',
|
||||
'spacebars', 'htmljs', 'blaze', 'webapp-hashing'], 'server');
|
||||
api.use(['underscore'], 'client');
|
||||
api.use(['application-configuration', 'follower-livedata'], {
|
||||
unordered: true
|
||||
});
|
||||
|
||||
// At response serving time, webapp uses browser-policy if it is loaded. If
|
||||
// browser-policy is loaded, then it must be loaded after webapp
|
||||
// (browser-policy depends on webapp). So we don't explicitly depend in any
|
||||
|
||||
@@ -701,39 +701,6 @@ var runWebAppServer = function () {
|
||||
httpServer.on('request', WebApp._timeoutAdjustmentRequestCallback);
|
||||
|
||||
|
||||
// For now, handle SIGHUP here. Later, this should be in some centralized
|
||||
// Meteor shutdown code.
|
||||
process.on('SIGHUP', Meteor.bindEnvironment(function () {
|
||||
shuttingDown = true;
|
||||
// tell others with websockets open that we plan to close this.
|
||||
// XXX: Eventually, this should be done with a standard meteor shut-down
|
||||
// logic path.
|
||||
httpServer.emit('meteor-closing');
|
||||
|
||||
httpServer.close(Meteor.bindEnvironment(function () {
|
||||
if (proxy) {
|
||||
try {
|
||||
proxy.call('removeBindingsForJob', process.env.GALAXY_JOB);
|
||||
} catch (e) {
|
||||
Log.error("Error removing bindings: " + e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
process.exit(0);
|
||||
|
||||
}, "On http server close failed"));
|
||||
|
||||
// Ideally we will close before this hits.
|
||||
Meteor.setTimeout(function () {
|
||||
Log.warn("Closed by SIGHUP but one or more HTTP requests may not have finished.");
|
||||
process.exit(1);
|
||||
}, 5000);
|
||||
|
||||
}, function (err) {
|
||||
console.log(err);
|
||||
process.exit(1);
|
||||
}));
|
||||
|
||||
// start up app
|
||||
_.extend(WebApp, {
|
||||
connectHandlers: packageAndAppHandlers,
|
||||
@@ -759,10 +726,6 @@ var runWebAppServer = function () {
|
||||
// middlewares and update __meteor_runtime_config__, then keep going to set up
|
||||
// actually serving HTML.
|
||||
main = function (argv) {
|
||||
// main happens post startup hooks, so we don't need a Meteor.startup() to
|
||||
// ensure this happens after the galaxy package is loaded.
|
||||
var AppConfig = Package["application-configuration"].AppConfig;
|
||||
|
||||
WebAppInternals.generateBoilerplate();
|
||||
|
||||
// only start listening after all the startup code has run.
|
||||
@@ -772,49 +735,6 @@ var runWebAppServer = function () {
|
||||
httpServer.listen(localPort, localIp, Meteor.bindEnvironment(function() {
|
||||
if (process.env.METEOR_PRINT_ON_LISTEN)
|
||||
console.log("LISTENING"); // must match run-app.js
|
||||
var proxyBinding;
|
||||
|
||||
AppConfig.configurePackage('webapp', function (configuration) {
|
||||
if (proxyBinding)
|
||||
proxyBinding.stop();
|
||||
if (configuration && configuration.proxy) {
|
||||
// TODO: We got rid of the place where this checks the app's
|
||||
// configuration, because this wants to be configured for some things
|
||||
// on a per-job basis. Discuss w/ teammates.
|
||||
proxyBinding = AppConfig.configureService(
|
||||
"proxy",
|
||||
"pre0",
|
||||
function (proxyService) {
|
||||
if (proxyService && ! _.isEmpty(proxyService)) {
|
||||
var proxyConf;
|
||||
// XXX Figure out a per-job way to specify bind location
|
||||
// (besides hardcoding the location for ADMIN_APP jobs).
|
||||
if (process.env.ADMIN_APP) {
|
||||
var bindPathPrefix = "";
|
||||
if (process.env.GALAXY_APP !== "panel") {
|
||||
bindPathPrefix = "/" + bindPathPrefix +
|
||||
encodeURIComponent(
|
||||
process.env.GALAXY_APP
|
||||
).replace(/\./g, '_');
|
||||
}
|
||||
proxyConf = {
|
||||
bindHost: process.env.GALAXY_NAME,
|
||||
bindPathPrefix: bindPathPrefix,
|
||||
requiresAuth: true
|
||||
};
|
||||
} else {
|
||||
proxyConf = configuration.proxy;
|
||||
}
|
||||
Log("Attempting to bind to proxy at " +
|
||||
proxyService);
|
||||
WebAppInternals.bindToProxy(_.extend({
|
||||
proxyEndpoint: proxyService
|
||||
}, proxyConf));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var callbacks = onListeningCallbacks;
|
||||
onListeningCallbacks = null;
|
||||
@@ -830,289 +750,6 @@ var runWebAppServer = function () {
|
||||
};
|
||||
|
||||
|
||||
var proxy;
|
||||
WebAppInternals.bindToProxy = function (proxyConfig) {
|
||||
var securePort = proxyConfig.securePort || 4433;
|
||||
var insecurePort = proxyConfig.insecurePort || 8080;
|
||||
var bindPathPrefix = proxyConfig.bindPathPrefix || "";
|
||||
// XXX also support galaxy-based lookup
|
||||
if (!proxyConfig.proxyEndpoint)
|
||||
throw new Error("missing proxyEndpoint");
|
||||
if (!proxyConfig.bindHost)
|
||||
throw new Error("missing bindHost");
|
||||
if (!process.env.GALAXY_JOB)
|
||||
throw new Error("missing $GALAXY_JOB");
|
||||
if (!process.env.GALAXY_APP)
|
||||
throw new Error("missing $GALAXY_APP");
|
||||
if (!process.env.LAST_START)
|
||||
throw new Error("missing $LAST_START");
|
||||
|
||||
// XXX rename pid argument to bindTo.
|
||||
// XXX factor out into a 'getPid' function in a 'galaxy' package?
|
||||
var pid = {
|
||||
job: process.env.GALAXY_JOB,
|
||||
lastStarted: +(process.env.LAST_START),
|
||||
app: process.env.GALAXY_APP
|
||||
};
|
||||
var myHost = os.hostname();
|
||||
|
||||
WebAppInternals.usingDdpProxy = true;
|
||||
|
||||
// This is run after packages are loaded (in main) so we can use
|
||||
// Follower.connect.
|
||||
if (proxy) {
|
||||
// XXX the concept here is that our configuration has changed and
|
||||
// we have connected to an entirely new follower set, which does
|
||||
// not have the state that we set up on the follower set that we
|
||||
// were previously connected to, and so we need to recreate all of
|
||||
// our bindings -- analogous to getting a SIGHUP and rereading
|
||||
// your configuration file. so probably this should actually tear
|
||||
// down the connection and make a whole new one, rather than
|
||||
// hot-reconnecting to a different URL.
|
||||
proxy.reconnect({
|
||||
url: proxyConfig.proxyEndpoint
|
||||
});
|
||||
} else {
|
||||
proxy = Package["follower-livedata"].Follower.connect(
|
||||
proxyConfig.proxyEndpoint, {
|
||||
group: "proxy"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var route = process.env.ROUTE;
|
||||
var ourHost = route.split(":")[0];
|
||||
var ourPort = +route.split(":")[1];
|
||||
|
||||
var outstanding = 0;
|
||||
var startedAll = false;
|
||||
var checkComplete = function () {
|
||||
if (startedAll && ! outstanding)
|
||||
Log("Bound to proxy.");
|
||||
};
|
||||
var makeCallback = function () {
|
||||
outstanding++;
|
||||
return function (err) {
|
||||
if (err)
|
||||
throw err;
|
||||
outstanding--;
|
||||
checkComplete();
|
||||
};
|
||||
};
|
||||
|
||||
// for now, have our (temporary) requiresAuth flag apply to all
|
||||
// routes created by this process.
|
||||
var requiresDdpAuth = !! proxyConfig.requiresAuth;
|
||||
var requiresHttpAuth = (!! proxyConfig.requiresAuth) &&
|
||||
(pid.app !== "panel" && pid.app !== "auth");
|
||||
|
||||
// XXX a current limitation is that we treat securePort and
|
||||
// insecurePort as a global configuration parameter -- we assume
|
||||
// that if the proxy wants us to ask for 8080 to get port 80 traffic
|
||||
// on our default hostname, that's the same port that we would use
|
||||
// to get traffic on some other hostname that our proxy listens
|
||||
// for. Likewise, we assume that if the proxy can receive secure
|
||||
// traffic for our domain, it can assume secure traffic for any
|
||||
// domain! Hopefully this will get cleaned up before too long by
|
||||
// pushing that logic into the proxy service, so we can just ask for
|
||||
// port 80.
|
||||
|
||||
// XXX BUG: if our configuration changes, and bindPathPrefix
|
||||
// changes, it appears that we will not remove the routes derived
|
||||
// from the old bindPathPrefix from the proxy (until the process
|
||||
// exits). It is not actually normal for bindPathPrefix to change,
|
||||
// certainly not without a process restart for other reasons, but
|
||||
// it'd be nice to fix.
|
||||
|
||||
_.each(routes, function (route) {
|
||||
var parsedUrl = url.parse(route.url, /* parseQueryString */ false,
|
||||
/* slashesDenoteHost aka workRight */ true);
|
||||
if (parsedUrl.protocol || parsedUrl.port || parsedUrl.search)
|
||||
throw new Error("Bad url");
|
||||
parsedUrl.host = null;
|
||||
parsedUrl.path = null;
|
||||
if (! parsedUrl.hostname) {
|
||||
parsedUrl.hostname = proxyConfig.bindHost;
|
||||
if (! parsedUrl.pathname)
|
||||
parsedUrl.pathname = "";
|
||||
if (! parsedUrl.pathname.indexOf("/") !== 0) {
|
||||
// Relative path
|
||||
parsedUrl.pathname = bindPathPrefix + parsedUrl.pathname;
|
||||
}
|
||||
}
|
||||
var version = "";
|
||||
|
||||
var AppConfig = Package["application-configuration"].AppConfig;
|
||||
version = AppConfig.getStarForThisJob() || "";
|
||||
|
||||
|
||||
var parsedDdpUrl = _.clone(parsedUrl);
|
||||
parsedDdpUrl.protocol = "ddp";
|
||||
// Node has a hardcoded list of protocols that get '://' instead
|
||||
// of ':'. ddp needs to be added to that whitelist. Until then, we
|
||||
// can set the undocumented attribute 'slashes' to get the right
|
||||
// behavior. It's not clear whether than is by design or accident.
|
||||
parsedDdpUrl.slashes = true;
|
||||
parsedDdpUrl.port = '' + securePort;
|
||||
var ddpUrl = url.format(parsedDdpUrl);
|
||||
|
||||
var proxyToHost, proxyToPort, proxyToPathPrefix;
|
||||
if (! _.has(route, 'forwardTo')) {
|
||||
proxyToHost = ourHost;
|
||||
proxyToPort = ourPort;
|
||||
proxyToPathPrefix = parsedUrl.pathname;
|
||||
} else {
|
||||
var parsedFwdUrl = url.parse(route.forwardTo, false, true);
|
||||
if (! parsedFwdUrl.hostname || parsedFwdUrl.protocol)
|
||||
throw new Error("Bad forward url");
|
||||
proxyToHost = parsedFwdUrl.hostname;
|
||||
proxyToPort = parseInt(parsedFwdUrl.port || "80");
|
||||
proxyToPathPrefix = parsedFwdUrl.pathname || "";
|
||||
}
|
||||
|
||||
if (route.ddp) {
|
||||
proxy.call('bindDdp', {
|
||||
pid: pid,
|
||||
bindTo: {
|
||||
ddpUrl: ddpUrl,
|
||||
insecurePort: insecurePort
|
||||
},
|
||||
proxyTo: {
|
||||
tags: [version],
|
||||
host: proxyToHost,
|
||||
port: proxyToPort,
|
||||
pathPrefix: proxyToPathPrefix + '/websocket'
|
||||
},
|
||||
requiresAuth: requiresDdpAuth
|
||||
}, makeCallback());
|
||||
}
|
||||
|
||||
if (route.http) {
|
||||
proxy.call('bindHttp', {
|
||||
pid: pid,
|
||||
bindTo: {
|
||||
host: parsedUrl.hostname,
|
||||
port: insecurePort,
|
||||
pathPrefix: parsedUrl.pathname
|
||||
},
|
||||
proxyTo: {
|
||||
tags: [version],
|
||||
host: proxyToHost,
|
||||
port: proxyToPort,
|
||||
pathPrefix: proxyToPathPrefix
|
||||
},
|
||||
requiresAuth: requiresHttpAuth
|
||||
}, makeCallback());
|
||||
|
||||
// Only make the secure binding if we've been told that the
|
||||
// proxy knows how terminate secure connections for us (has an
|
||||
// appropriate cert, can bind the necessary port..)
|
||||
if (proxyConfig.securePort !== null) {
|
||||
proxy.call('bindHttp', {
|
||||
pid: pid,
|
||||
bindTo: {
|
||||
host: parsedUrl.hostname,
|
||||
port: securePort,
|
||||
pathPrefix: parsedUrl.pathname,
|
||||
ssl: true
|
||||
},
|
||||
proxyTo: {
|
||||
tags: [version],
|
||||
host: proxyToHost,
|
||||
port: proxyToPort,
|
||||
pathPrefix: proxyToPathPrefix
|
||||
},
|
||||
requiresAuth: requiresHttpAuth
|
||||
}, makeCallback());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
startedAll = true;
|
||||
checkComplete();
|
||||
};
|
||||
|
||||
// (Internal, unsupported interface -- subject to change)
|
||||
//
|
||||
// Listen for HTTP and/or DDP traffic and route it somewhere. Only
|
||||
// takes effect when using a proxy service.
|
||||
//
|
||||
// 'url' is the traffic that we want to route, interpreted relative to
|
||||
// the default URL where this app has been told to serve itself. It
|
||||
// may not have a scheme or port, but it may have a host and a path,
|
||||
// and if no host is provided the path need not be absolute. The
|
||||
// following cases are possible:
|
||||
//
|
||||
// //somehost.com
|
||||
// All incoming traffic for 'somehost.com'
|
||||
// //somehost.com/foo/bar
|
||||
// All incoming traffic for 'somehost.com', but only when
|
||||
// the first two path components are 'foo' and 'bar'.
|
||||
// /foo/bar
|
||||
// Incoming traffic on our default host, but only when the
|
||||
// first two path components are 'foo' and 'bar'.
|
||||
// foo/bar
|
||||
// Incoming traffic on our default host, but only when the path
|
||||
// starts with our default path prefix, followed by 'foo' and
|
||||
// 'bar'.
|
||||
//
|
||||
// (Yes, these scheme-less URLs that start with '//' are legal URLs.)
|
||||
//
|
||||
// You can select either DDP traffic, HTTP traffic, or both. Both
|
||||
// secure and insecure traffic will be gathered (assuming the proxy
|
||||
// service is capable, eg, has appropriate certs and port mappings).
|
||||
//
|
||||
// With no 'forwardTo' option, the traffic is received by this process
|
||||
// for service by the hooks in this 'webapp' package. The original URL
|
||||
// is preserved (that is, if you bind "/a", and a user visits "/a/b",
|
||||
// the app receives a request with a path of "/a/b", not a path of
|
||||
// "/b").
|
||||
//
|
||||
// With 'forwardTo', the process is instead sent to some other remote
|
||||
// host. The URL is adjusted by stripping the path components in 'url'
|
||||
// and putting the path components in the 'forwardTo' URL in their
|
||||
// place. For example, if you forward "//somehost/a" to
|
||||
// "//otherhost/x", and the user types "//somehost/a/b" into their
|
||||
// browser, then otherhost will receive a request with a Host header
|
||||
// of "somehost" and a path of "/x/b".
|
||||
//
|
||||
// The routing continues until this process exits. For now, all of the
|
||||
// routes must be set up ahead of time, before the initial
|
||||
// registration with the proxy. Calling addRoute from the top level of
|
||||
// your JS should do the trick.
|
||||
//
|
||||
// When multiple routes are present that match a given request, the
|
||||
// most specific route wins. When routes with equal specificity are
|
||||
// present, the proxy service will distribute the traffic between
|
||||
// them.
|
||||
//
|
||||
// options may be:
|
||||
// - ddp: if true, the default, include DDP traffic. This includes
|
||||
// both secure and insecure traffic, and both websocket and sockjs
|
||||
// transports.
|
||||
// - http: if true, the default, include HTTP/HTTPS traffic.
|
||||
// - forwardTo: if provided, should be a URL with a host, optional
|
||||
// path and port, and no scheme (the scheme will be derived from the
|
||||
// traffic type; for now it will always be a http or ws connection,
|
||||
// never https or wss, but we could add a forwardSecure flag to
|
||||
// re-encrypt).
|
||||
var routes = [];
|
||||
WebAppInternals.addRoute = function (url, options) {
|
||||
options = _.extend({
|
||||
ddp: true,
|
||||
http: true
|
||||
}, options || {});
|
||||
|
||||
if (proxy)
|
||||
// In the future, lift this restriction
|
||||
throw new Error("Too late to add routes");
|
||||
|
||||
routes.push(_.extend({ url: url }, options));
|
||||
};
|
||||
|
||||
// Receive traffic on our default URL.
|
||||
WebAppInternals.addRoute("");
|
||||
|
||||
runWebAppServer();
|
||||
|
||||
|
||||
|
||||
204
tools/auth.js
204
tools/auth.js
@@ -181,7 +181,8 @@ var getSession = function (sessionData, domain) {
|
||||
|
||||
// types:
|
||||
// - "meteor-account": a login to your Meteor Account
|
||||
// - "galaxy": a login to a Galaxy
|
||||
// We previously used:
|
||||
// - "galaxy": a login to a legacy Galaxy prototype server
|
||||
var ensureSessionType = function (session, type) {
|
||||
if (! _.has(session, 'type'))
|
||||
session.type = type;
|
||||
@@ -259,53 +260,6 @@ var removePendingRevoke = function (domain, tokenIds) {
|
||||
writeSessionData(data);
|
||||
};
|
||||
|
||||
var tryRevokeGalaxyTokens = function (domain, tokenIds, options) {
|
||||
var oauthInfo = fetchGalaxyOAuthInfo(domain, options.timeout);
|
||||
if (oauthInfo) {
|
||||
url = oauthInfo.revokeUri;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
var result = httpHelpers.request({
|
||||
url: url,
|
||||
method: "POST",
|
||||
form: {
|
||||
tokenId: tokenIds.join(',')
|
||||
},
|
||||
useSessionHeader: true,
|
||||
timeout: options.timeout
|
||||
});
|
||||
} catch (e) {
|
||||
// most likely we don't have a net connection
|
||||
return false;
|
||||
}
|
||||
var response = result.response;
|
||||
|
||||
if (response.statusCode === 200 &&
|
||||
response.body) {
|
||||
try {
|
||||
var body = JSON.parse(response.body);
|
||||
if (body.tokenRevoked) {
|
||||
// Server confirms that the tokens have been revoked. Checking for a
|
||||
// `tokenRevoked` key in the response confirms that we hit an actual
|
||||
// galaxy auth server that understands that we were trying to revoke some
|
||||
// tokens, not just a random URL that happened to return a 200
|
||||
// response.
|
||||
|
||||
// (Be careful to reread session data in case httpHelpers changed it)
|
||||
removePendingRevoke(domain, tokenIds);
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// If there are any logged out (pendingRevoke) tokens that haven't
|
||||
// been sent to the server for revocation yet, try to send
|
||||
// them. Reads the session file and then writes it back out to
|
||||
@@ -364,9 +318,10 @@ var tryRevokeOldTokens = function (options) {
|
||||
}
|
||||
return;
|
||||
} else if (session.type === "galaxy") {
|
||||
if (! tryRevokeGalaxyTokens(domain, tokenIds, options)) {
|
||||
logoutFailWarning(domain);
|
||||
}
|
||||
// These are tokens from a legacy Galaxy prototype, which cannot be
|
||||
// revoked (because the prototype no longer exists), but we can at least
|
||||
// remove them from the file.
|
||||
removePendingRevoke(domain, tokenIds);
|
||||
} else {
|
||||
// don't know how to revoke tokens of this type
|
||||
logoutFailWarning(domain);
|
||||
@@ -375,40 +330,6 @@ var tryRevokeOldTokens = function (options) {
|
||||
});
|
||||
};
|
||||
|
||||
// Sends a request to https://<galaxyName>:<DISCOVERY_PORT> to find out the
|
||||
// galaxy's OAuth client id and redirect_uri that should be used for
|
||||
// authorization codes for this galaxy. Returns an object with keys
|
||||
// 'oauthClientId', 'redirectUri', and 'revokeUri', or null if the
|
||||
// request failed.
|
||||
//
|
||||
// 'timeout' is an optional request timeout in milliseconds.
|
||||
var fetchGalaxyOAuthInfo = function (galaxyName, timeout) {
|
||||
var galaxyAuthUrl = 'https://' + galaxyName + ':' +
|
||||
config.getDiscoveryPort() + '/_GALAXYAUTH_';
|
||||
try {
|
||||
var result = httpHelpers.request({
|
||||
url: galaxyAuthUrl,
|
||||
json: true,
|
||||
// on by default in our version of request, but just in case
|
||||
strictSSL: true,
|
||||
followRedirect: false,
|
||||
timeout: timeout || 5000
|
||||
});
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result.response.statusCode === 200 &&
|
||||
result.body &&
|
||||
result.body.oauthClientId &&
|
||||
result.body.redirectUri &&
|
||||
result.body.revokeUri) {
|
||||
return result.body;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var sendAuthorizeRequest = function (clientId, redirectUri, state) {
|
||||
var authCodeUrl = config.getOauthUrl() + "/authorize?" +
|
||||
querystring.stringify({
|
||||
@@ -506,77 +427,6 @@ var oauthFlow = function (conn, options) {
|
||||
}
|
||||
};
|
||||
|
||||
// Uses meteor accounts to log in to the specified galaxy. Returns an
|
||||
// object with keys `token` and `tokenId` if the login was
|
||||
// successful. If an error occurred, returns one of:
|
||||
// { error: 'access-denied' }
|
||||
// { error: 'no-galaxy' }
|
||||
// { error: 'no-account-server' }
|
||||
var logInToGalaxy = function (galaxyName) {
|
||||
var oauthInfo = fetchGalaxyOAuthInfo(galaxyName);
|
||||
if (! oauthInfo) {
|
||||
return { error: 'no-galaxy' };
|
||||
}
|
||||
|
||||
var galaxyClientId = oauthInfo.oauthClientId;
|
||||
var galaxyRedirect = oauthInfo.redirectUri;
|
||||
|
||||
// If the redirect URI is not in the DNS namespace that belongs to the
|
||||
// Galaxy, then something is wrong.
|
||||
if (url.parse(galaxyRedirect).hostname !== galaxyName) {
|
||||
// XXX It's more like 'bad-galaxy' than 'no-galaxy'.
|
||||
return { error: 'no-galaxy' };
|
||||
}
|
||||
|
||||
// Ask the accounts server for an authorization code.
|
||||
var crypto = require('crypto');
|
||||
var session = crypto.randomBytes(16).toString('hex');
|
||||
var stateInfo = { session: session };
|
||||
|
||||
var authorizeResult;
|
||||
|
||||
try {
|
||||
authorizeResult = sendAuthorizeRequest(
|
||||
galaxyClientId,
|
||||
galaxyRedirect,
|
||||
encodeURIComponent(JSON.stringify(stateInfo))
|
||||
);
|
||||
} catch (err) {
|
||||
return { error: err.message };
|
||||
}
|
||||
|
||||
// Ask the galaxy to log us in with our auth code.
|
||||
try {
|
||||
var galaxyResult = httpHelpers.request({
|
||||
url: authorizeResult.location,
|
||||
method: 'GET',
|
||||
strictSSL: true,
|
||||
headers: {
|
||||
cookie: 'GALAXY_OAUTH_SESSION=' + session +
|
||||
'; GALAXY_USER_AGENT_TOOL=' +
|
||||
encodeURIComponent(JSON.stringify(utils.getAgentInfo()))
|
||||
}
|
||||
});
|
||||
var body = JSON.parse(galaxyResult.body);
|
||||
} catch (e) {
|
||||
return { error: (body && body.error) || 'no-galaxy' };
|
||||
}
|
||||
var response = galaxyResult.response;
|
||||
|
||||
// 'access-denied' isn't exactly right because it's possible that the galaxy
|
||||
// went down since our last request, but close enough.
|
||||
|
||||
if (response.statusCode !== 200 ||
|
||||
! body ||
|
||||
! _.has(galaxyResult.setCookie, 'GALAXY_AUTH'))
|
||||
return { error: (body && body.error) || 'access-denied' };
|
||||
|
||||
return {
|
||||
token: galaxyResult.setCookie.GALAXY_AUTH,
|
||||
tokenId: body.tokenId
|
||||
};
|
||||
};
|
||||
|
||||
// Prompt the user for a password, and then log in. Returns true if a
|
||||
// successful login was accomplished, else false.
|
||||
//
|
||||
@@ -681,11 +531,9 @@ exports.loginCommand = withAccountsConnection(function (options,
|
||||
config.printUniverseBanner();
|
||||
|
||||
var data = readSessionData();
|
||||
var galaxy = options.galaxy;
|
||||
|
||||
if (! galaxy &&
|
||||
(! getSession(data, config.getAccountsDomain()).token ||
|
||||
options.overwriteExistingToken)) {
|
||||
if (! getSession(data, config.getAccountsDomain()).token ||
|
||||
options.overwriteExistingToken) {
|
||||
var loginOptions = {};
|
||||
|
||||
if (options.email) {
|
||||
@@ -707,43 +555,13 @@ exports.loginCommand = withAccountsConnection(function (options,
|
||||
}
|
||||
}
|
||||
|
||||
// XXX Make the galaxy login not do a login if there is an existing token, just like MA
|
||||
if (galaxy) {
|
||||
var galaxyLoginResult = logInToGalaxy(galaxy);
|
||||
if (galaxyLoginResult.error) {
|
||||
// XXX add human readable error messages
|
||||
var failedLoginMsg = "\nLogin to ' + galaxy + ' failed. ";
|
||||
if (galaxyLoginResult.error === 'unauthorized') {
|
||||
Console.error(
|
||||
failedLoginMsg + 'You are not authorized for this galaxy.');
|
||||
} else if (galaxyLoginResult.error === 'no_oauth_server') {
|
||||
Console.error(
|
||||
failedLoginMsg + 'The galaxy could not contact Meteor Accounts.');
|
||||
} else if (galaxyLoginResult.error === 'no_identity') {
|
||||
Console.error(
|
||||
failedLoginMsg + 'Your login information could not be found.');
|
||||
} else {
|
||||
Console.error(failedLoginMsg + 'Error: ' + galaxyLoginResult.error );
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
data = readSessionData(); // be careful to reread data file after RPC
|
||||
var session = getSession(data, galaxy);
|
||||
ensureSessionType(session, "galaxy");
|
||||
session.token = galaxyLoginResult.token;
|
||||
session.tokenId = galaxyLoginResult.tokenId;
|
||||
writeSessionData(data);
|
||||
}
|
||||
|
||||
tryRevokeOldTokens({ firstTry: true, connection: connection });
|
||||
|
||||
data = readSessionData();
|
||||
Console.error();
|
||||
Console.error("Logged in" + (galaxy ? " to " + galaxy : "") +
|
||||
(currentUsername(data) ?
|
||||
" as " + currentUsername(data) : "") + ".",
|
||||
"Thanks for being a Meteor developer!");
|
||||
Console.error("Logged in" +
|
||||
(currentUsername(data) ? " as " + currentUsername(data) : "") +
|
||||
". Thanks for being a Meteor developer!");
|
||||
return 0;
|
||||
});
|
||||
|
||||
|
||||
@@ -36,10 +36,9 @@
|
||||
// running in standalone mode (after setting appropriate environment
|
||||
// variables as documented in README)
|
||||
//
|
||||
// /server/.bundle_version.txt: contains the dev_bundle version that
|
||||
// legacy (read: current) Galaxy version read in order to set
|
||||
// NODE_PATH to point to arch-specific builds of binary node modules
|
||||
// (primarily this is for node-fibers)
|
||||
// /server/.bundle_version.txt: contains the dev_bundle version that the meteor
|
||||
// deploy server reads in order to set NODE_PATH to point to arch-specific
|
||||
// builds of binary node modules
|
||||
//
|
||||
// XXX in the future one program (which must be a server-type
|
||||
// architecture) will be designated as the 'init' program. The
|
||||
@@ -1648,11 +1647,6 @@ _.extend(ServerTarget.prototype, {
|
||||
write: function (builder, options) {
|
||||
var self = this;
|
||||
|
||||
// Pick a start script name
|
||||
// XXX base it on the name of the target
|
||||
var scriptName = 'start.sh';
|
||||
builder.reserve(scriptName);
|
||||
|
||||
// This is where the dev_bundle will be downloaded and unpacked
|
||||
builder.reserve('dependencies');
|
||||
|
||||
@@ -1718,21 +1712,10 @@ _.extend(ServerTarget.prototype, {
|
||||
return;
|
||||
}
|
||||
|
||||
var devBundleVersion =
|
||||
files.readFile(
|
||||
files.pathJoin(files.getDevBundle(), '.bundle_version.txt'), 'utf8');
|
||||
devBundleVersion = devBundleVersion.split('\n')[0];
|
||||
|
||||
var Packages = isopackets.load('dev-bundle-fetcher');
|
||||
var script = Packages["dev-bundle-fetcher"].DevBundleFetcher.script();
|
||||
script = script.replace(/##PLATFORM##/g, platform);
|
||||
script = script.replace(/##BUNDLE_VERSION##/g, devBundleVersion);
|
||||
script = script.replace(/##IMAGE##/g, imageControlFile);
|
||||
script = script.replace(/##RUN_FILE##/g, 'boot.js');
|
||||
builder.write(scriptName, { data: new Buffer(script, 'utf8'),
|
||||
executable: true });
|
||||
|
||||
return scriptName;
|
||||
// Nothing actually pays attention to the `path` field for a server program
|
||||
// in star.json any more, so it might as well be boot.js. (It used to be
|
||||
// start.sh, a script included for the legacy Galaxy prototype.)
|
||||
return 'boot.js';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1793,7 +1776,6 @@ var writeTargetToPath = function (name, target, outputPath, options) {
|
||||
// options:
|
||||
// - includeNodeModulesSymlink: bool
|
||||
// - builtBy: vanity identification string to write into metadata
|
||||
// - controlProgram: name of the control program (should be a target name)
|
||||
// - releaseName: The Meteor release version
|
||||
// - getRelativeTargetPath: see doc at ServerTarget.write
|
||||
var writeSiteArchive = function (targets, outputPath, options) {
|
||||
@@ -1804,25 +1786,19 @@ var writeSiteArchive = function (targets, outputPath, options) {
|
||||
symlink: options.includeNodeModulesSymlink
|
||||
});
|
||||
|
||||
if (options.controlProgram && ! (options.controlProgram in targets))
|
||||
throw new Error("controlProgram '" + options.controlProgram +
|
||||
"' is not the name of a target?");
|
||||
|
||||
try {
|
||||
var json = {
|
||||
format: "site-archive-pre1",
|
||||
builtBy: options.builtBy,
|
||||
programs: [],
|
||||
control: options.controlProgram || undefined,
|
||||
meteorRelease: options.releaseName
|
||||
};
|
||||
|
||||
// Tell Galaxy what version of the dependency kit we're using, so
|
||||
// it can load the right modules. (Include this even if we copied
|
||||
// or symlinked a node_modules, since that's probably enough for
|
||||
// it to work in spite of the presence of node_modules for the
|
||||
// wrong arch). The place we stash this is grody for temporary
|
||||
// reasons of backwards compatibility.
|
||||
// Tell the deploy server what version of the dependency kit we're using, so
|
||||
// it can load the right modules. (Include this even if we copied or
|
||||
// symlinked a node_modules, since that's probably enough for it to work in
|
||||
// spite of the presence of node_modules for the wrong arch). The place we
|
||||
// stash this is grody for temporary reasons of backwards compatibility.
|
||||
builder.write(files.pathJoin('server', '.bundle_version.txt'), {
|
||||
file: files.pathJoin(files.getDevBundle(), '.bundle_version.txt')
|
||||
});
|
||||
@@ -1868,7 +1844,6 @@ var writeSiteArchive = function (targets, outputPath, options) {
|
||||
json.programs.push(writeTargetToPath(name, target, builder.buildPath, {
|
||||
includeNodeModulesSymlink: options.includeNodeModulesSymlink,
|
||||
builtBy: options.builtBy,
|
||||
controlProgram: options.controlProgram,
|
||||
releaseName: options.releaseName,
|
||||
getRelativeTargetPath: options.getRelativeTargetPath
|
||||
}));
|
||||
@@ -1925,9 +1900,6 @@ var writeSiteArchive = function (targets, outputPath, options) {
|
||||
* - hasCachedBundle: true if we already have a cached bundle stored in
|
||||
* /build. When true, we only build the new client targets in the bundle.
|
||||
*
|
||||
* - requireControlProgram: true if we need to include a "ctl" program in
|
||||
* the bundle. This is required for an old prototype of Galaxy.
|
||||
*
|
||||
* Returns an object with keys:
|
||||
* - errors: A buildmessage.MessageSet, or falsy if bundling succeeded.
|
||||
* - serverWatchSet: Information about server files and paths that were
|
||||
@@ -2044,15 +2016,6 @@ exports.bundle = function (options) {
|
||||
targets.server = server;
|
||||
}
|
||||
|
||||
// Create a "control program". This is required for an old version of
|
||||
// Galaxy.
|
||||
var controlProgram = null;
|
||||
if (options.requireControlProgram) {
|
||||
var target = makeServerTarget("ctl");
|
||||
targets["ctl"] = target;
|
||||
controlProgram = "ctl";
|
||||
}
|
||||
|
||||
// Hack to let servers find relative paths to clients. Should find
|
||||
// another solution eventually (probably some kind of mount
|
||||
// directive that mounts the client bundle in the server at runtime)
|
||||
@@ -2076,7 +2039,6 @@ exports.bundle = function (options) {
|
||||
var writeOptions = {
|
||||
includeNodeModulesSymlink: includeNodeModulesSymlink,
|
||||
builtBy: builtBy,
|
||||
controlProgram: controlProgram,
|
||||
releaseName: releaseName,
|
||||
getRelativeTargetPath: getRelativeTargetPath
|
||||
};
|
||||
|
||||
@@ -23,8 +23,8 @@ var execFileSync = require('./utils.js').execFileSync;
|
||||
var Console = require('./console.js').Console;
|
||||
var projectContextModule = require('./project-context.js');
|
||||
|
||||
// The architecture used by Galaxy servers; it's the architecture used
|
||||
// by 'meteor deploy'.
|
||||
// The architecture used by MDG's hosted servers; it's the architecture used by
|
||||
// 'meteor deploy'.
|
||||
var DEPLOY_ARCH = 'os.linux.x86_64';
|
||||
|
||||
// The default port that the development server listens on.
|
||||
@@ -55,14 +55,6 @@ var qualifySitename = function (site) {
|
||||
return site;
|
||||
};
|
||||
|
||||
// Given a (non necessarily fully qualified) site name from the
|
||||
// command line, return true if the site is hosted by a Galaxy, else
|
||||
// false.
|
||||
var hostedWithGalaxy = function (site) {
|
||||
var site = qualifySitename(site);
|
||||
return !! require('./deploy-galaxy.js').discoverGalaxy(site);
|
||||
};
|
||||
|
||||
// Display a message showing valid Meteor architectures.
|
||||
var showInvalidArchMsg = function (arch) {
|
||||
Console.info("Invalid architecture: " + arch);
|
||||
@@ -917,13 +909,8 @@ main.registerCommand({
|
||||
var site = qualifySitename(options.args[0]);
|
||||
config.printUniverseBanner();
|
||||
|
||||
if (hostedWithGalaxy(site)) {
|
||||
var deployGalaxy = require('./deploy-galaxy.js');
|
||||
mongoUrl = deployGalaxy.temporaryMongoUrl(site);
|
||||
} else {
|
||||
mongoUrl = deploy.temporaryMongoUrl(site);
|
||||
usedMeteorAccount = true;
|
||||
}
|
||||
mongoUrl = deploy.temporaryMongoUrl(site);
|
||||
usedMeteorAccount = true;
|
||||
|
||||
if (! mongoUrl)
|
||||
// temporaryMongoUrl() will have printed an error message
|
||||
@@ -997,14 +984,9 @@ main.registerCommand({
|
||||
'delete': { type: Boolean, short: 'D' },
|
||||
debug: { type: Boolean },
|
||||
settings: { type: String },
|
||||
star: { type: String },
|
||||
// No longer supported, but we still parse it out so that we can
|
||||
// print a custom error message.
|
||||
password: { type: String },
|
||||
// Shouldn't be documented until the Galaxy release. Marks the
|
||||
// application as an admin app, so that it will be available in
|
||||
// Galaxy admin interface.
|
||||
admin: { type: Boolean },
|
||||
// Override architecture to deploy whatever stuff we have locally, even if
|
||||
// it contains binary packages that should be incompatible. A hack to allow
|
||||
// people to deploy from checkout or do other weird shit. We are not
|
||||
@@ -1012,42 +994,24 @@ main.registerCommand({
|
||||
'override-architecture-with-local' : { type: Boolean }
|
||||
},
|
||||
requiresApp: function (options) {
|
||||
return options.delete || options.star ? false : true;
|
||||
return ! options.delete;
|
||||
},
|
||||
catalogRefresh: new catalog.Refresh.Never()
|
||||
}, function (options) {
|
||||
var site = qualifySitename(options.args[0]);
|
||||
config.printUniverseBanner();
|
||||
var useGalaxy = hostedWithGalaxy(site);
|
||||
var deployGalaxy;
|
||||
|
||||
if (options.delete) {
|
||||
if (useGalaxy) {
|
||||
deployGalaxy = require('./deploy-galaxy.js');
|
||||
return deployGalaxy.deleteApp(site);
|
||||
} else {
|
||||
return deploy.deleteApp(site);
|
||||
}
|
||||
return deploy.deleteApp(site);
|
||||
}
|
||||
|
||||
if (options.password) {
|
||||
if (useGalaxy) {
|
||||
Console.error("Galaxy does not support --password.");
|
||||
} else {
|
||||
Console.error(
|
||||
"Setting passwords on apps is no longer supported. Now there are " +
|
||||
Console.error(
|
||||
"Setting passwords on apps is no longer supported. Now there are " +
|
||||
"user accounts and your apps are associated with your account so " +
|
||||
"that only you (and people you designate) can access them. See the " +
|
||||
Console.command("'meteor claim'") + " and " +
|
||||
Console.command("'meteor authorized'") + " commands.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
var starball = options.star;
|
||||
if (starball && ! useGalaxy) {
|
||||
// XXX it would be nice to support this for non-Galaxy deploys too
|
||||
Console.error("--star: only supported when deploying to Galaxy.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1074,8 +1038,7 @@ main.registerCommand({
|
||||
|
||||
var projectContext = new projectContextModule.ProjectContext({
|
||||
projectDir: options.appDir,
|
||||
serverArchitectures: _.uniq([buildArch, archinfo.host()]),
|
||||
requireControlProgram: useGalaxy
|
||||
serverArchitectures: _.uniq([buildArch, archinfo.host()])
|
||||
});
|
||||
|
||||
main.captureAndExit("=> Errors while initializing project:", function () {
|
||||
@@ -1089,25 +1052,12 @@ main.registerCommand({
|
||||
serverArch: buildArch
|
||||
};
|
||||
|
||||
var deployResult;
|
||||
if (useGalaxy) {
|
||||
deployGalaxy = require('./deploy-galaxy.js');
|
||||
deployResult = deployGalaxy.deploy({
|
||||
projectContext: projectContext,
|
||||
app: site,
|
||||
settingsFile: options.settings,
|
||||
starball: starball,
|
||||
buildOptions: buildOptions,
|
||||
admin: options.admin
|
||||
});
|
||||
} else {
|
||||
deployResult = deploy.bundleAndDeploy({
|
||||
projectContext: projectContext,
|
||||
site: site,
|
||||
settingsFile: options.settings,
|
||||
buildOptions: buildOptions
|
||||
});
|
||||
}
|
||||
var deployResult = deploy.bundleAndDeploy({
|
||||
projectContext: projectContext,
|
||||
site: site,
|
||||
settingsFile: options.settings,
|
||||
buildOptions: buildOptions
|
||||
});
|
||||
|
||||
if (deployResult === 0) {
|
||||
auth.maybePrintRegistrationLink({
|
||||
@@ -1130,27 +1080,11 @@ main.registerCommand({
|
||||
name: 'logs',
|
||||
minArgs: 1,
|
||||
maxArgs: 1,
|
||||
options: {
|
||||
// XXX once Galaxy is released, document this
|
||||
stream: { type: Boolean, short: 'f' }
|
||||
},
|
||||
catalogRefresh: new catalog.Refresh.Never()
|
||||
}, function (options) {
|
||||
var site = qualifySitename(options.args[0]);
|
||||
|
||||
if (hostedWithGalaxy(site)) {
|
||||
var deployGalaxy = require('./deploy-galaxy.js');
|
||||
var ret = deployGalaxy.logs({
|
||||
app: site,
|
||||
streaming: options.stream
|
||||
});
|
||||
if (options.stream && ret === null) {
|
||||
throw new main.WaitForExit;
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return deploy.logs(site);
|
||||
}
|
||||
return deploy.logs(site);
|
||||
});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1191,14 +1125,6 @@ main.registerCommand({
|
||||
auth.pollForRegistrationCompletion();
|
||||
var site = qualifySitename(options.args[0]);
|
||||
|
||||
if (hostedWithGalaxy(site)) {
|
||||
Console.error(
|
||||
"Sites hosted on Galaxy do not have an authorized user list. " +
|
||||
"Instead, go to your Galaxy dashboard to change the authorized users " +
|
||||
"of your Galaxy.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (! auth.isLoggedIn()) {
|
||||
Console.error(
|
||||
"You must be logged in for that. Try " +
|
||||
@@ -1239,12 +1165,6 @@ main.registerCommand({
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (hostedWithGalaxy(site)) {
|
||||
Console.error(
|
||||
"Sorry, you can't claim sites that are hosted on Galaxy.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return deploy.claim(site);
|
||||
});
|
||||
|
||||
@@ -1587,10 +1507,7 @@ main.registerCommand({
|
||||
main.registerCommand({
|
||||
name: 'login',
|
||||
options: {
|
||||
email: { type: Boolean },
|
||||
// Undocumented: get credentials on a specific Galaxy. Do we still
|
||||
// need this?
|
||||
galaxy: { type: String }
|
||||
email: { type: Boolean }
|
||||
},
|
||||
catalogRefresh: new catalog.Refresh.Never()
|
||||
}, function (options) {
|
||||
|
||||
@@ -24,8 +24,8 @@ var compiler = exports;
|
||||
// You should also update this whenever you update any of the packages used
|
||||
// directly by the isopack creation process (eg js-analyze) since they do not
|
||||
// end up as watched dependencies. (At least for now, packages only used in
|
||||
// target creation (eg minifiers and dev-bundle-fetcher) don't require you to
|
||||
// update BUILT_BY, though you will need to quit and rerun "meteor run".)
|
||||
// target creation (eg minifiers) don't require you to update BUILT_BY, though
|
||||
// you will need to quit and rerun "meteor run".)
|
||||
compiler.BUILT_BY = 'meteor/15';
|
||||
|
||||
// This is a list of all possible architectures that a build can target. (Client
|
||||
|
||||
@@ -19,8 +19,7 @@ var tropohouse = require('./tropohouse.js');
|
||||
// We're not quite there yet though:
|
||||
// - When developing locally, you may need to set DISCOVERY_PORT (see
|
||||
// getDiscoveryPort below)
|
||||
// - GALAXY can still be used to override Galaxy discovery, and
|
||||
// DELPOY_HOSTNAME can still be set to override classic-style
|
||||
// - DEPLOY_HOSTNAME can still be set to override classic-style
|
||||
// deploys
|
||||
// - The update/warehouse system hasn't been touched and still has its
|
||||
// hardcoded URLs for now (update.meteor.com and
|
||||
|
||||
@@ -1,419 +0,0 @@
|
||||
var Future = require('fibers/future');
|
||||
var files = require('./files.js');
|
||||
var config = require('./config.js');
|
||||
var path = require('path');
|
||||
var isopackets = require("./isopackets.js");
|
||||
var httpHelpers = require('./http-helpers.js');
|
||||
var auth = require('./auth.js');
|
||||
var url = require('url');
|
||||
var _ = require('underscore');
|
||||
var buildmessage = require('./buildmessage.js');
|
||||
var ServiceConnection = require('./service-connection.js');
|
||||
var stats = require('./stats.js');
|
||||
var Console = require('./console.js').Console;
|
||||
|
||||
// If 'error' is an exception that we know how to report in a
|
||||
// user-friendly way, print an approprite message to stderr and return
|
||||
// an appropriate exit status for a command. Else rethrow error.
|
||||
//
|
||||
// galaxyName should be the name of the galaxy that we're talking to.
|
||||
// If messages is provided, it is a map from DDP error names to
|
||||
// human-readable explanation to use.
|
||||
var handleError = function (error, galaxyName, messages) {
|
||||
messages = messages || {};
|
||||
|
||||
if (error.errorType === "Meteor.Error") {
|
||||
var msg = messages[error.error];
|
||||
if (msg)
|
||||
Console.error(msg);
|
||||
else if (error.message)
|
||||
Console.error("Denied: " + error.message);
|
||||
return 1;
|
||||
} else if (error.errorType === "DDP.ConnectionError") {
|
||||
// If we have an http/https URL for a galaxyName instead of a
|
||||
// proper galaxyName (which is what the code in this file
|
||||
// currently passes), strip off the scheme and trailing slash.
|
||||
var m = galaxyName.match(/^https?:\/\/(.*[^\/])\/?$/);
|
||||
if (m)
|
||||
galaxyName = m[1];
|
||||
|
||||
Console.error(galaxyName + ": connection failed");
|
||||
return 1;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns a ServiceConnection to a galaxy service that is authenticated
|
||||
// from the credential cache.
|
||||
//
|
||||
// - galaxy: the name of the galaxy to connect to, as returned by
|
||||
// discoverGalaxy (as described there, should probably be a galaxy
|
||||
// name, but currently is a https or http URL)
|
||||
// - service: the service to connect to within the Galaxy, such as
|
||||
// 'ultraworld' or 'log-reader'.
|
||||
var galaxyServiceConnection = function (galaxy, service) {
|
||||
var endpointUrl = galaxy + "/" + service;
|
||||
var parsedEndpoint = url.parse(endpointUrl);
|
||||
var authToken = auth.getSessionToken(parsedEndpoint.hostname);
|
||||
|
||||
// XXX XXX higher up on the stack, we need to get the galaxy name
|
||||
// from the hostname of endpointUrl, and run the login command for
|
||||
// that galaxy.
|
||||
if (! authToken)
|
||||
throw new Error("not logged in to galaxy?");
|
||||
|
||||
return new ServiceConnection(endpointUrl, {
|
||||
headers: {
|
||||
cookie: "GALAXY_AUTH=" + authToken
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Determine if a particular site is hosted by Galaxy, and if so, by
|
||||
// which Galaxy. 'app' should be a hostname, like 'myapp.meteor.com'
|
||||
// or 'mysite.com'. Returns the base URL for the Galaxy
|
||||
// (https://[galaxyname], or possibly http://[galaxyname] if running
|
||||
// locally). The URL will not have a trailing slash. Returns null if
|
||||
// the site is not hosted by Galaxy.
|
||||
//
|
||||
// The result is cached, so there is no penality for calling this
|
||||
// function multiple times (during the same run of the
|
||||
// tool). (Assuming you wait for the first call to complete before
|
||||
// making the subsequent calls. The caching doesn't kick in until the
|
||||
// first call returns.)
|
||||
//
|
||||
// XXX in the future, should probably return the name of the Galaxy,
|
||||
// rather than a URL.
|
||||
//
|
||||
// XXX at many places in this file we call discoverGalaxy and don't
|
||||
// check its return value. This is safe because we expect that
|
||||
// command.js will have already called discoverGalaxy on the same app
|
||||
// before we get here, and gotten a satisfactory value, which is now
|
||||
// cached. But it's not great -- add better error handling.
|
||||
var discoveryCache = {};
|
||||
exports.discoverGalaxy = function (app) {
|
||||
var cacheKey = app;
|
||||
if (_.has(discoveryCache, cacheKey))
|
||||
return discoveryCache[cacheKey];
|
||||
|
||||
app = app + ":" + config.getDiscoveryPort();
|
||||
var discoveryUrl = "https://" + app + "/_GALAXY_";
|
||||
var fut = new Future();
|
||||
|
||||
if (process.env.GALAXY)
|
||||
return process.env.GALAXY;
|
||||
|
||||
// At some point we may want to send a version in the request so that galaxy
|
||||
// can respond differently to different versions of meteor.
|
||||
httpHelpers.request({
|
||||
url: discoveryUrl,
|
||||
json: true,
|
||||
strictSSL: true,
|
||||
// We don't want to be confused by, eg, a non-Galaxy-hosted site which
|
||||
// redirects to a Galaxy-hosted site.
|
||||
followRedirect: false
|
||||
}, function (err, resp, body) {
|
||||
if (! err &&
|
||||
resp.statusCode === 200 &&
|
||||
body &&
|
||||
_.has(body, "galaxyDiscoveryVersion") &&
|
||||
_.has(body, "galaxyUrl") &&
|
||||
(body.galaxyDiscoveryVersion === "galaxy-discovery-pre0")) {
|
||||
var result = body.galaxyUrl;
|
||||
|
||||
if (result.indexOf("https://") === -1)
|
||||
result = "https://" + result;
|
||||
|
||||
if (result[result.length - 1] === "/")
|
||||
result = result.substring(0, result.length - 1);
|
||||
|
||||
fut.return(result);
|
||||
} else {
|
||||
fut.return(null);
|
||||
}
|
||||
});
|
||||
|
||||
var result = fut.wait();
|
||||
discoveryCache[cacheKey] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
exports.deleteApp = function (app) {
|
||||
var galaxy = exports.discoverGalaxy(app);
|
||||
var conn = galaxyServiceConnection(galaxy, "ultraworld");
|
||||
|
||||
try {
|
||||
conn.call("destroyApp", app);
|
||||
Console.info("Deleted.");
|
||||
} catch (e) {
|
||||
return handleError(e, galaxy);
|
||||
} finally {
|
||||
conn.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Returns exit code for deploy command.
|
||||
//
|
||||
// options:
|
||||
// - app
|
||||
// - appDir
|
||||
// - settingsFile
|
||||
// - buildOptions
|
||||
// - starball
|
||||
// XXX refactor this to separate the "maybe bundle" part from "actually deploy"
|
||||
// so we can be careful to not rely on any of the app dir context when
|
||||
// in --star mode.
|
||||
exports.deploy = function (options) {
|
||||
var conn = null;
|
||||
|
||||
try {
|
||||
var tmpdir = files.mkdtemp('deploy');
|
||||
var buildDir = path.join(tmpdir, 'build');
|
||||
var topLevelDirName = path.basename(options.appDir);
|
||||
var bundlePath = path.join(buildDir, topLevelDirName);
|
||||
var bundler = require('./bundler.js');
|
||||
var starball;
|
||||
|
||||
var settings = null;
|
||||
var messages = buildmessage.capture({
|
||||
title: "preparing to deploy",
|
||||
rootPath: process.cwd()
|
||||
}, function () {
|
||||
if (options.settingsFile)
|
||||
settings = files.getSettings(options.settingsFile);
|
||||
});
|
||||
|
||||
// Don't try to connect to galaxy before the bundle is
|
||||
// done. Because bundling doesn't yield, this will cause the
|
||||
// connection to time out. Eventually we'd like to have bundle
|
||||
// yield, so that we can connect (and make sure auth works)
|
||||
// concurrent with bundling.
|
||||
|
||||
if (! options.starball && ! messages.hasMessages()) {
|
||||
Console.info('Deploying ' + options.app + '. Bundling...');
|
||||
var bundleResult = bundler.bundle({
|
||||
projectContext: options.projectContext,
|
||||
outputPath: bundlePath,
|
||||
buildOptions: options.buildOptions,
|
||||
requireControlProgram: true
|
||||
});
|
||||
|
||||
if (bundleResult.errors) {
|
||||
messages = bundleResult.errors;
|
||||
} else {
|
||||
stats.recordPackages({
|
||||
what: "sdk.deploy",
|
||||
projectContext: options.projectContext,
|
||||
site: options.app
|
||||
});
|
||||
|
||||
// S3 (which is what's likely on the other end our upload)
|
||||
// requires a content-length header for HTTP PUT uploads. That
|
||||
// means that we have to actually tgz up the bundle before we
|
||||
// can start the upload rather than streaming it. S3 has an
|
||||
// alternate API for doing chunked uploads, but (a) it has a
|
||||
// minimum chunk size of 5 MB, so it doesn't help us much
|
||||
// (many/most stars will be smaller than that), and (b) it's
|
||||
// nonstandard, so we'd have to bake in S3's specific
|
||||
// scheme. Doesn't seem worthwhile for now, so just tar to a
|
||||
// temporary directory. If stars get radically bigger then it
|
||||
// might be worthwhile to tar to memory and spill to S3 every
|
||||
// 5MB.
|
||||
starball = path.join(tmpdir, topLevelDirName + ".tar.gz");
|
||||
files.createTarball(bundlePath, starball);
|
||||
}
|
||||
} else {
|
||||
starball = options.starball;
|
||||
}
|
||||
|
||||
if (messages.hasMessages()) {
|
||||
Console.info("\nErrors prevented deploying:");
|
||||
Console.info(messages.formatMessages());
|
||||
return 1;
|
||||
}
|
||||
|
||||
Console.info('Uploading...');
|
||||
|
||||
var galaxy = exports.discoverGalaxy(options.app);
|
||||
conn = galaxyServiceConnection(galaxy, "ultraworld");
|
||||
|
||||
var created = true;
|
||||
var appConfig = {};
|
||||
if (settings !== null)
|
||||
appConfig.settings = settings;
|
||||
|
||||
if (options.admin)
|
||||
appConfig.admin = true;
|
||||
|
||||
try {
|
||||
conn.call('createApp', options.app, appConfig);
|
||||
} catch (e) {
|
||||
if (e.errorType === 'Meteor.Error' && e.error === 'already-exists') {
|
||||
// Cool, it already exists. No problem. Just set the settings
|
||||
// if they were passed. We explicitly check for undefined
|
||||
// because we want to allow you to unset settings by passing
|
||||
// an empty file.
|
||||
if (appConfig.settings !== undefined) {
|
||||
conn.call('updateAppConfiguration', options.app, appConfig);
|
||||
}
|
||||
created = false;
|
||||
} else {
|
||||
return handleError(e, galaxy);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the upload information from Galaxy. It's a surprise if this
|
||||
// fails (we already know the app exists).
|
||||
try {
|
||||
var info = conn.call('beginUploadStar', options.app,
|
||||
bundleResult.starManifest);
|
||||
} catch (e) {
|
||||
return handleError(e, galaxy);
|
||||
}
|
||||
|
||||
// Upload
|
||||
// XXX copied from galaxy/tool/galaxy.js
|
||||
var fileSize = files.stat(starball).size;
|
||||
var fileStream = files.createReadStream(starball);
|
||||
var future = new Future;
|
||||
var req = httpHelpers.request({
|
||||
method: "PUT",
|
||||
url: info.put,
|
||||
headers: { 'content-length': fileSize,
|
||||
'content-type': 'application/octet-stream' },
|
||||
strictSSL: true
|
||||
}, function (error, response, body) {
|
||||
if (error || ((response.statusCode !== 200)
|
||||
&& (response.statusCode !== 201))) {
|
||||
if (error && error.message)
|
||||
Console.error("Upload failed: " + error.message);
|
||||
else
|
||||
Console.error("Upload failed" +
|
||||
(response.statusCode ?
|
||||
" (" + response.statusCode + ")" : ""));
|
||||
future['return'](false);
|
||||
} else
|
||||
future['return'](true);
|
||||
});
|
||||
|
||||
fileStream.pipe(req);
|
||||
var uploadSucceeded = future.wait();
|
||||
if (! uploadSucceeded)
|
||||
return 1;
|
||||
|
||||
try {
|
||||
var result = conn.call('completeUploadStar', info.id);
|
||||
} catch (e) {
|
||||
return handleError(e, galaxy, {
|
||||
'no-such-upload': 'Upload request expired. Try again.'
|
||||
});
|
||||
}
|
||||
|
||||
if (created)
|
||||
Console.error(options.app + ": created app\n");
|
||||
|
||||
Console.error(options.app + ": " +
|
||||
"pushed revision " + result.serial);
|
||||
return 0;
|
||||
} finally {
|
||||
// Close the connection to Galaxy (otherwise Node will continue running).
|
||||
conn && conn.close();
|
||||
}
|
||||
};
|
||||
|
||||
// options:
|
||||
// - app
|
||||
// - streaming (BOOL)
|
||||
//
|
||||
// The log messages are printed. Returns a command exit code, or if
|
||||
// streaming is true and streaming was successfully started, returns
|
||||
// null.
|
||||
exports.logs = function (options) {
|
||||
var galaxy = exports.discoverGalaxy(options.app);
|
||||
var logReader = galaxyServiceConnection(galaxy, "log-reader");
|
||||
|
||||
try {
|
||||
var lastLogId = null;
|
||||
var Log = isopackets.load('logging').logging.Log;
|
||||
|
||||
// XXX we're cheating a bit here, relying on the server sending
|
||||
// the log messages in order
|
||||
var ok = logReader.connection.registerStore('logs', {
|
||||
update: function (msg) {
|
||||
// Ignore all messages but 'changed'
|
||||
if (msg.msg !== 'changed')
|
||||
return;
|
||||
var obj = msg.fields.obj;
|
||||
lastLogId = msg.fields.id;
|
||||
obj = Log.parse(obj);
|
||||
obj && console.log(Log.format(obj, {color: true}));
|
||||
}
|
||||
});
|
||||
|
||||
if (! ok)
|
||||
throw new Error("Can't listen to messages on the logs collection");
|
||||
|
||||
var logsSubscription = null;
|
||||
try {
|
||||
logsSubscription =
|
||||
logReader.subscribeAndWait("logsForApp", options.app,
|
||||
{ streaming: options.streaming });
|
||||
} catch (e) {
|
||||
return handleError(e, galaxy, {
|
||||
"no-such-app": "No such app: " + options.app
|
||||
});
|
||||
}
|
||||
|
||||
// In case of reconnect recover the state so user sees only new logs.
|
||||
// Only set up the onReconnect handler after the subscribe and wait
|
||||
// has returned; if we set it up before, then we'll end up with two
|
||||
// subscriptions, because the onReconnect handler will run for the
|
||||
// first time before the subscribeAndWait returns.
|
||||
logReader.connection.onReconnect = function () {
|
||||
logsSubscription && logsSubscription.stop();
|
||||
var opts = { streaming: options.streaming };
|
||||
if (lastLogId)
|
||||
opts.resumeAfterId = lastLogId;
|
||||
// Don't use subscribeAndWait here; it'll deadlock. We can't
|
||||
// process the sub messages until `onReconnect` returns, and
|
||||
// `onReconnect` won't return unless the sub messages have been
|
||||
// processed. There's no reason we need to wait for the sub to be
|
||||
// ready here anyway.
|
||||
// XXX correctly handle errors on resubscribe
|
||||
logsSubscription = logReader.connection.subscribe(
|
||||
"logsForApp",
|
||||
options.app,
|
||||
opts
|
||||
);
|
||||
};
|
||||
|
||||
return options.streaming ? null : 0;
|
||||
} finally {
|
||||
// If not streaming, close the connection to log-reader so that
|
||||
// Node can exit cleanly. If streaming, leave the connection open
|
||||
// so that we continue to get logs.
|
||||
if (! options.streaming) {
|
||||
logReader.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// On failure, prints a message to stderr and returns null. Otherwise,
|
||||
// returns a temporary authenticated Mongo URL allowing access to this
|
||||
// site's database.
|
||||
exports.temporaryMongoUrl = function (app) {
|
||||
var galaxy = exports.discoverGalaxy(app);
|
||||
var conn = galaxyServiceConnection(galaxy, "ultraworld");
|
||||
|
||||
try {
|
||||
var mongoUrl = conn.call('getTemporaryMongoUrl', app);
|
||||
} catch (e) {
|
||||
handleError(e, galaxy);
|
||||
return null;
|
||||
} finally {
|
||||
conn.close();
|
||||
}
|
||||
|
||||
return mongoUrl;
|
||||
};
|
||||
@@ -359,7 +359,6 @@ Options:
|
||||
--delete, -D permanently delete this deployment
|
||||
--debug deploy in debug mode (don't minify, etc)
|
||||
--settings set optional data for Meteor.settings
|
||||
--star a star (tarball) to deploy instead of the current Meteor app
|
||||
|
||||
|
||||
>>> logs
|
||||
|
||||
@@ -50,7 +50,6 @@ var ISOPACKETS = {
|
||||
'mongo': ['mongo'],
|
||||
'ejson': ['ejson'],
|
||||
'minifiers': ['minifiers'],
|
||||
'dev-bundle-fetcher': ['dev-bundle-fetcher'],
|
||||
'constraint-solver': ['constraint-solver'],
|
||||
'cordova-support': ['boilerplate-generator', 'logging', 'webapp-hashing',
|
||||
'xmlbuilder'],
|
||||
|
||||
@@ -113,9 +113,6 @@ _.extend(ProjectContext.prototype, {
|
||||
// package names.
|
||||
self._upgradePackageNames = options.upgradePackageNames;
|
||||
|
||||
// Set when deploying to a previous Galaxy prototype.
|
||||
self._requireControlProgram = options.requireControlProgram;
|
||||
|
||||
// Set by publishing commands to ensure that published packages always have
|
||||
// a web.cordova slice (because we aren't yet smart enough to just default
|
||||
// to using the web.browser slice instead or make a common 'web' slice).
|
||||
@@ -501,7 +498,6 @@ _.extend(ProjectContext.prototype, {
|
||||
self._addAppConstraints(depsAndConstraints);
|
||||
self._addLocalPackageConstraints(depsAndConstraints);
|
||||
self._addReleaseConstraints(depsAndConstraints);
|
||||
self._addGalaxyPrototypeConstraints(depsAndConstraints);
|
||||
return depsAndConstraints;
|
||||
},
|
||||
|
||||
@@ -540,17 +536,6 @@ _.extend(ProjectContext.prototype, {
|
||||
});
|
||||
},
|
||||
|
||||
// We only need to build ctl if deploying to the legacy Galaxy
|
||||
// prototype. (Note that this means that we will need a new constraint
|
||||
// solution when deploying vs when running locally. This code will be deleted
|
||||
// soon anyway.)
|
||||
_addGalaxyPrototypeConstraints: function (depsAndConstraints) {
|
||||
var self = this;
|
||||
if (self._requireControlProgram) {
|
||||
depsAndConstraints.deps.push('ctl');
|
||||
}
|
||||
},
|
||||
|
||||
_getAnticipatedPrereleases: function (rootConstraints, cachedVersions) {
|
||||
var self = this;
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ var logsOrMongoForApp = function (sandbox, command, appName, options) {
|
||||
// I suppose it's possible that we don't have any INFO messages in
|
||||
// the logs, but it seems unlikely. Every time we run a command we
|
||||
// hit /_GALAXY_ on the site.
|
||||
// XXX This is no longer true now that we've removed legacy Galaxy
|
||||
// prototype support, so if this causes test flakiness, it may
|
||||
// need to be tweaked.
|
||||
matchString = 'INFO';
|
||||
} else {
|
||||
throw new Error('Command must be "logs" or "mongo"');
|
||||
|
||||
Reference in New Issue
Block a user