Merge pull request #7055 from meteor/glasser/galaxy-discovery

Galaxy discovery and other tool cleanup.
This commit is contained in:
Ben Newman
2016-05-16 17:28:23 -04:00
6 changed files with 116 additions and 294 deletions

View File

@@ -1145,7 +1145,6 @@ to this command.`);
} else {
// remote mode
var site = qualifySitename(options.args[0]);
config.printUniverseBanner();
mongoUrl = deploy.temporaryMongoUrl(site);
usedMeteorAccount = true;
@@ -1240,7 +1239,6 @@ main.registerCommand({
catalogRefresh: new catalog.Refresh.Never()
}, function (options, {rawOptions}) {
var site = options.args[0];
config.printUniverseBanner();
if (options.delete) {
return deploy.deleteApp(site);
@@ -1358,7 +1356,6 @@ main.registerCommand({
return 1;
}
config.printUniverseBanner();
auth.pollForRegistrationCompletion();
var site = qualifySitename(options.args[0]);
@@ -1390,7 +1387,6 @@ main.registerCommand({
maxArgs: 1,
catalogRefresh: new catalog.Refresh.Never()
}, function (options) {
config.printUniverseBanner();
auth.pollForRegistrationCompletion();
var site = qualifySitename(options.args[0]);
@@ -1956,8 +1952,6 @@ main.registerCommand({
throw new main.ShowUsage;
}
config.printUniverseBanner();
var username = options.add || options.remove;
var conn = loggedInAccountsConnectionOrPrompt(

View File

@@ -555,8 +555,6 @@ exports.doInteractivePasswordLogin = doInteractivePasswordLogin;
exports.loginCommand = withAccountsConnection(function (options,
connection) {
config.printUniverseBanner();
var data = readSessionData();
if (! getSession(data, config.getAccountsDomain()).token ||
@@ -593,8 +591,6 @@ exports.loginCommand = withAccountsConnection(function (options,
});
exports.logoutCommand = function (options) {
config.printUniverseBanner();
var data = readSessionData();
var wasLoggedIn = !! loggedIn(data);
logOutAllSessions(data);
@@ -686,7 +682,6 @@ exports.registrationUrl = function () {
};
exports.whoAmICommand = function (options) {
config.printUniverseBanner();
auth.pollForRegistrationCompletion();
var data = readSessionData();

View File

@@ -8,179 +8,54 @@ var tropohouse = require('../packaging/tropohouse.js');
// deploying apps to the MDG free hosting sandbox, publishing packages,
// getting an ssh access to a build farm. These functions need
// configuration.
//
// The idea is that eventually, the `meteor` will take only one
// configuration parameter, the "universe" it is talking to, which
// defaults to "www.meteor.com". In a git checkout it can be set by
// creating a file at the root of the checkout called "universe" that
// contains the name of the universe you wish to use. Then, all other
// needed configuration is derived from the universe name.
//
// We're not quite there yet though:
// - When developing locally, you may need to set DISCOVERY_PORT (see
// getDiscoveryPort below)
// - 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
// warehouse.meteor.com). Really, it's debatable whether these
// should (necessarily) change when you change your universe name.
var universe;
var getUniverse = function () {
if (! universe) {
universe = "www.meteor.com";
if (files.inCheckout()) {
var p = files.pathJoin(files.getCurrentToolsDir(), 'universe');
if (files.exists(p)) {
universe = files.readFile(p, 'utf8').trim();
}
}
}
return universe;
};
var isLocalUniverse = function () {
return !! getUniverse().match(/^localhost(:([\d]+))?$/);
};
var localhostOffset = function (portOffset) {
var match = getUniverse().match(/^localhost(:([\d]+))?$/);
if (! match) {
throw new Error("not a local universe?");
}
return "localhost:" + (parseInt(match[2] || "80") + portOffset);
};
var getAuthServiceHost = function () {
if (! isLocalUniverse()) {
return universe;
} else {
// Special case for local development. Point
// $METEOR_CHECKOUT/universe at the place where you are running
// frontpage (eg, localhost:3000), and run the accounts server ten
// port numbers higher. Like so:
// cd meteor-accounts
// ROOT_URL=http://localhost:3010/auth curmeteor -p 3010
return localhostOffset(10);
}
};
// Given a hostname, add "http://" or "https://" as
// appropriate. (localhost gets http; anything else is always https.)
var addScheme = function (host) {
if (host.match(/^localhost(:\d+)?$/)) {
return "http://" + host;
} else {
return "https://" + host;
}
};
var config = exports;
_.extend(exports, {
// True if this the production universe (www.meteor.com)
isProduction: function () {
return getUniverse() === "www.meteor.com";
},
// The current universe name. Should be used for cosmetic purposes
// only (displaying to the user). If you want to programmatically
// derive configuration from it, add a new method to this file.
getUniverse: function () {
return getUniverse();
},
// Base URL for Meteor Accounts OAuth services, typically
// "https://www.meteor.com/oauth2". Endpoints include /authorize and
// /token.
// Base URL for Meteor Accounts OAuth services. Endpoints include /authorize
// and /token.
getOauthUrl: function () {
return addScheme(getAuthServiceHost()) + "/oauth2";
return "https://www.meteor.com/oauth2";
},
// Base URL for Meteor Accounts API, typically
// "https://www.meteor.com/api/v1". Endpoints include '/login' and
// Base URL for Meteor Accounts API. Endpoints include '/login' and
// '/logoutById'.
getAccountsApiUrl: function () {
return addScheme(getAuthServiceHost()) + "/api/v1";
return "https://www.meteor.com/api/v1";
},
// URL for the DDP interface to Meteor Accounts, typically
// "https://www.meteor.com/auth". (Really should be a ddp:// URL --
// we'll get there soon enough.)
// URL for the DDP interface to Meteor Accounts.
getAuthDDPUrl: function () {
return addScheme(getAuthServiceHost()) + "/auth";
return "https://www.meteor.com/auth";
},
// URL for the DDP interface to the meteor build farm, typically
// "https://build.meteor.com".
getBuildFarmUrl: function () {
if (process.env.METEOR_BUILD_FARM_URL) {
return process.env.METEOR_BUILD_FARM_URL;
}
var host = config.getBuildFarmDomain();
return addScheme(host);
return process.env.METEOR_BUILD_FARM_URL || "https://build.meteor.com";
},
getBuildFarmDomain: function () {
if (process.env.METEOR_BUILD_FARM_URL) {
var parsed = url.parse(process.env.METEOR_BUILD_FARM_URL);
return parsed.host;
} else {
return getUniverse().replace(/^www\./, 'build.');
}
return url.parse(config.getBuildFarmUrl()).host;
},
// URL for the DDP interface to the package server, typically
// "https://packages.meteor.com". (Really should be a ddp:// URL --
// we'll get there soon enough.)
//
// When running everything locally, run the package server at the
// base universe port number (that is, the Meteor Accounts port
// number) plus 20.
// "https://packages.meteor.com".
getPackageServerUrl: function () {
if (process.env.METEOR_PACKAGE_SERVER_URL) {
return process.env.METEOR_PACKAGE_SERVER_URL;
}
var host = config.getPackageServerDomain();
return addScheme(host);
return process.env.METEOR_PACKAGE_SERVER_URL ||
"https://packages.meteor.com";
},
getPackageServerDomain: function () {
if (isLocalUniverse()) {
return localhostOffset(20);
} else {
if (process.env.METEOR_PACKAGE_SERVER_URL) {
var parsed = url.parse(process.env.METEOR_PACKAGE_SERVER_URL);
return parsed.host;
} else {
return getUniverse().replace(/^www\./, 'packages.');
}
}
return url.parse(config.getPackageServerUrl()).host;
},
getPackageStatsServerUrl: function () {
if (process.env.METEOR_PACKAGE_STATS_SERVER_URL) {
return process.env.METEOR_PACKAGE_STATS_SERVER_URL;
}
var host = config.getPackageStatsServerDomain();
return addScheme(host);
return process.env.METEOR_PACKAGE_STATS_SERVER_URL ||
"https://activity.meteor.com";
},
getPackageStatsServerDomain: function () {
if (process.env.METEOR_PACKAGE_STATS_SERVER_URL) {
return url.parse(process.env.METEOR_PACKAGE_STATS_SERVER_URL).hostname;
}
if (isLocalUniverse()) {
return localhostOffset(30);
} else {
return getUniverse().replace(/^www\./, 'activity.');
}
return url.parse(config.getPackageStatsServerUrl()).host;
},
// Note: this is NOT guaranteed to return a distinct prefix for every
@@ -246,56 +121,7 @@ _.extend(exports, {
// use. This is used as a key for storing your Meteor Accounts
// login token.
getAccountsDomain: function () {
return getUniverse();
},
getDeployHostname: function () {
return process.env.DEPLOY_HOSTNAME || "meteor.com";
},
getFullAppName: function(appName) {
var domain = process.env.DEPLOY_DOMAIN || this.getDeployHostname();
if (appName.indexOf(".") === -1) {
return appName + "." + domain;
}
return appName;
},
// Deploy URL for MDG free hosting, eg 'https://deploy.meteor.com'.
getDeployUrl: function () {
var host;
// Support the old DEPLOY_HOSTNAME environment variable for a
// while longer. Soon, let's remove this in favor of the universe
// scheme.
if (process.env.DEPLOY_HOSTNAME) {
host = process.env.DEPLOY_HOSTNAME;
if (host.match(/^http/)) {
// allow it to contain a URL scheme
return host;
}
} else {
// Otherwise, base it on the universe.
if (isLocalUniverse()) {
throw new Error("local development of deploy server not supported");
} else {
host = getUniverse().replace(/^www\./, 'deploy.');
}
}
return addScheme(host);
},
// URL from which the update manifest may be fetched, eg
// 'https://update.meteor.com/manifest.json'
getUpdateManifestUrl: function () {
if (isLocalUniverse()) {
// localhost can't run the manifest server
u = "www.meteor.com";
}
var host = getUniverse().replace(/^www\./, 'update.');
return addScheme(host) + "/manifest.json";
return "www.meteor.com";
},
// Path to file that contains our credentials for any services that
@@ -305,32 +131,5 @@ _.extend(exports, {
// METEOR_SESSION_FILE is for automated testing purposes only.
return process.env.METEOR_SESSION_FILE ||
files.pathJoin(files.getHomeDir(), '.meteorsession');
},
// Port to use when querying URLs for the deploy server that backs
// them, and for querying oauth clients for their oauth information
// (so we can log into them).
//
// In production this should always be 443 (we *must*
// cryptographically authenticate the server answering the query),
// but this can be inconvenient for local development since 443 is a
// privileged port, so you can set DISCOVERY_PORT to override. (A
// better solution would probably be to spin up a local VM.)
getDiscoveryPort: function () {
if (process.env.DISCOVERY_PORT) {
return parseInt(process.env.DISCOVERY_PORT);
} else {
return 443;
}
},
// It's easy to forget that you're in an alternate universe (and
// that that is the reason you're not seeing your deploys). If not
// in production mode, print a quick hint about the universe you're
// in.
printUniverseBanner: function () {
if (! config.isProduction()) {
process.stderr.write('[Universe: ' + config.getUniverse() + ']\n');
}
}
});

View File

@@ -42,6 +42,8 @@ const CAPABILITIES = ['showDeployMessages', 'canTransferAuthorization'];
// - bodyStream: if provided, a stream to use as the request body
// - any other parameters accepted by the node 'request' module, for example
// 'qs' to set query string parameters
// - printDeployURL: provided if we should show the deploy URL; set this
// for the first RPC of any user command
//
// Waits until server responds, then returns an object with the
// following keys:
@@ -66,10 +68,16 @@ var deployRpc = function (options) {
}
options.qs = _.extend({}, options.qs, {capabilities: CAPABILITIES});
const deployURLBase = getDeployURL(options.site).await();
if (options.printDeployURL) {
Console.info("Talking to Galaxy servers at " + deployURLBase);
}
// XXX: Reintroduce progress for upload
try {
var result = httpHelpers.request(_.extend(options, {
url: config.getDeployUrl() + '/' + options.operation +
url: deployURLBase + '/' + options.operation +
(options.site ? ('/' + options.site) : ''),
method: options.method || 'GET',
bodyStream: options.bodyStream,
@@ -148,7 +156,8 @@ var authedRpc = function (options) {
operation: 'info',
site: rpcOptions.site,
expectPayload: [],
qs: options.qs
qs: options.qs,
printDeployURL: options.printDeployURL
});
if (infoResult.statusCode === 401 && rpcOptions.promptIfAuthFails) {
@@ -374,7 +383,8 @@ var bundleAndDeploy = function (options) {
site: site,
preflight: true,
promptIfAuthFails: promptIfAuthFails,
qs: options.rawOptions
qs: options.rawOptions,
printDeployURL: true
});
if (preflight.errorMessage) {
@@ -466,7 +476,6 @@ var bundleAndDeploy = function (options) {
dns.resolve(hostname, 'CNAME', function (err, cnames) {
if (err || cnames[0] !== 'origin.meteor.com') {
dns.resolve(hostname, 'A', function (err, addresses) {
console.log('and here')
if (err || addresses[0] !== '107.22.210.133') {
Console.info('-------------');
Console.info(
@@ -494,7 +503,8 @@ var deleteApp = function (site) {
method: 'DELETE',
operation: 'deploy',
site: site,
promptIfAuthFails: true
promptIfAuthFails: true,
printDeployURL: true
});
if (result.errorMessage) {
@@ -519,7 +529,8 @@ var checkAuthThenSendRpc = function (site, operation, what) {
operation: operation,
site: site,
preflight: true,
promptIfAuthFails: true
promptIfAuthFails: true,
printDeployURL: true
});
if (preflight.errorMessage) {
@@ -625,7 +636,8 @@ var listAuthorized = function (site) {
var result = deployRpc({
operation: 'info',
site: site,
expectPayload: []
expectPayload: [],
printDeployURL: true
});
if (result.errorMessage) {
Console.error("Couldn't get authorized users list: " + result.errorMessage);
@@ -676,7 +688,8 @@ var changeAuthorized = function (site, action, username) {
operation: 'authorized',
site: site,
qs: {[action]: username},
promptIfAuthFails: true
promptIfAuthFails: true,
printDeployURL: true
});
if (result.errorMessage) {
@@ -705,7 +718,8 @@ var claim = function (site) {
// straight away (at a cost of an extra REST call)
var infoResult = deployRpc({
operation: 'info',
site: site
site: site,
printDeployURL: true
});
if (infoResult.statusCode === 404) {
Console.error(
@@ -800,6 +814,82 @@ var listSites = function () {
return 0;
};
// Given a hostname, add "http://" or "https://" as
// appropriate. (localhost gets http; anything else is always https.)
function addScheme(hostOrURL) {
if (hostOrURL.match(/^http/)) {
return hostOrURL;
} else if (hostOrURL.match(/^localhost(:\d+)?$/)) {
return "http://" + hostOrURL;
} else {
return "https://" + hostOrURL;
}
};
// Maps from "site" to Promise<deploy URL>, so we don't have to re-ping on each
// RPC (even if the calls to getDeployURL overlap).
const galaxyDiscoveryCache = new Map;
// getDeployURL returns the a Promise for the base deploy URL for the given app.
// "app" may be falsey for certain RPCs (eg meteor list-sites).
function getDeployURL(site) {
// Always trust explicitly configuration via env.
if (process.env.DEPLOY_HOSTNAME) {
return Promise.resolve(addScheme(process.env.DEPLOY_HOSTNAME));
}
const defaultURL = "https://galaxy.meteor.com";
// No site? Just use the default.
if (!site) {
return Promise.resolve(defaultURL);
}
// If we have a site, we can try to do Galaxy discovery.
// Do we already have an answer?
if (galaxyDiscoveryCache.has(site)) {
return galaxyDiscoveryCache.get(site);
}
// Otherwise, try https first, then http, then just use the default.
const p = discoverGalaxy(site, "https")
.catch(() => discoverGalaxy(site, "http"))
.catch(() => defaultURL);
galaxyDiscoveryCache.set(site, p);
return p;
}
// discoverGalaxy returns the URL to use for Galaxy discovery, or an error if it
// couldn't be fetched.
async function discoverGalaxy(site, scheme) {
const discoveryURL =
scheme + "://" + site + "/.well-known/meteor/deploy-url";
// If httpHelpers.request throws, the returned Promise will reject, which is
// fine.
const { response, body } = 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
});
if (response.statusCode !== 200) {
throw new Error("bad status code: " + response.statusCode);
}
if (!body) {
throw new Error("response had no body");
}
if (body.galaxyDiscoveryVersion !== "galaxy-1") {
throw new Error(
"unexpected galaxyDiscoveryVersion: " + body.galaxyDiscoveryVersion);
}
if (!_.has(body, "deployURL")) {
throw new Error("no deployURL");
}
return body.deployURL;
}
exports.bundleAndDeploy = bundleAndDeploy;
exports.deleteApp = deleteApp;

View File

@@ -36,54 +36,3 @@ selftest.define("organizations - logged out", function () {
run.stop();
});
// For now, this test only runs from checkout with a universe file
// pointing to a testing meteor-accounts server (e.g. one deployed with
// Meteor.settings.testing = true).
selftest.define("organizations", ["net", "slow", "checkout"], function () {
var s = new Sandbox;
testUtils.login(s, "test", "testtest");
// Create an organization for the test.
var orgName = testUtils.createOrganization("test", "testtest");
// Add a nonexistent user.
var run = s.run("admin", "members",
orgName, "--add", testUtils.randomString(15));
run.waitSecs(commandTimeoutSecs);
run.matchErr("user does not exist");
run.expectExit(1);
// Add a user to a nonexistent org.
run = s.run("admin", "members",
testUtils.randomString(15), "--add", "testtest");
run.waitSecs(commandTimeoutSecs);
run.matchErr("Organization does not exist");
run.expectExit(1);
// Add a real user to a real org.
run = s.run("admin", "members", orgName, "--add", "testtest");
run.waitSecs(commandTimeoutSecs);
run.match("testtest added to organization " + orgName);
run.expectExit(0);
// Try to show a nonexistent organization.
run = s.run("admin", "members", testUtils.randomString(15));
run.waitSecs(commandTimeoutSecs);
run.matchErr("Organization does not exist");
run.expectExit(1);
// 'show-organization' should show the right members, and
// 'list-organization' should show that 'test' is a member.
run = s.run("admin", "members", orgName);
run.waitSecs(commandTimeoutSecs);
run.read("test\ntesttest\n");
run.expectExit(0);
run = s.run("admin", "list-organizations");
run.waitSecs(commandTimeoutSecs);
run.match(orgName + "\n");
run.expectExit(0);
testUtils.logout(s);
});

View File

@@ -48,11 +48,6 @@ exports.logout = function (s) {
run.expectExit(0);
};
exports.getUserId = function (s) {
var data = JSON.parse(s.readSessionFile());
return data.sessions[config.getUniverse()].userId;
};
var registrationUrlRegexp =
/https:\/\/www\.meteor\.com\/setPassword\?([a-zA-Z0-9\+\/]+)/;
exports.registrationUrlRegexp = registrationUrlRegexp;