diff --git a/src/apptask.js b/src/apptask.js index 49ca140b4..5055430d5 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -11,8 +11,6 @@ exports = module.exports = { _createAppDir: createAppDir, _deleteAppDir: deleteAppDir, _verifyManifest: verifyManifest, - _registerSubdomains: registerSubdomains, - _unregisterSubdomains: unregisterSubdomains, _waitForDnsPropagation: waitForDnsPropagation }; @@ -333,82 +331,6 @@ function removeIcon(app, callback) { callback(null); } -function registerSubdomains(app, overwrite, callback) { - assert.strictEqual(typeof app, 'object'); - assert.strictEqual(typeof overwrite, 'boolean'); - assert.strictEqual(typeof callback, 'function'); - - sysinfo.getServerIp(function (error, ip) { - if (error) return callback(error); - - const allDomains = [ { subdomain: app.location, domain: app.domain }].concat(app.alternateDomains).concat(app.aliasDomains); - - debugApp(app, `registerSubdomain: Will register ${JSON.stringify(allDomains)}`); - - async.eachSeries(allDomains, function (domain, iteratorDone) { - async.retry({ times: 200, interval: 5000 }, function (retryCallback) { - debugApp(app, 'Registering subdomain: %s%s', domain.subdomain ? (domain.subdomain + '.') : '', domain.domain); - - // get the current record before updating it - domains.getDnsRecords(domain.subdomain, domain.domain, 'A', function (error, values) { - if (error && error.reason === BoxError.EXTERNAL_ERROR) return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain })); // try again - if (error && error.reason === BoxError.ACCESS_DENIED) return retryCallback(null, new BoxError(BoxError.ACCESS_DENIED, error.message, { domain })); - if (error && error.reason === BoxError.NOT_FOUND) return retryCallback(null, new BoxError(BoxError.NOT_FOUND, error.message, { domain })); - if (error) return retryCallback(null, new BoxError(BoxError.EXTERNAL_ERROR, error.message, domain)); // give up for other errors - - if (values.length !== 0 && values[0] === ip) return retryCallback(null); // up-to-date - - // refuse to update any existing DNS record for custom domains that we did not create - if (values.length !== 0 && !overwrite) return retryCallback(null, new BoxError(BoxError.ALREADY_EXISTS, 'DNS Record already exists', { domain })); - - domains.upsertDnsRecords(domain.subdomain, domain.domain, 'A', [ ip ], function (error) { - if (error && (error.reason === BoxError.BUSY || error.reason === BoxError.EXTERNAL_ERROR)) { - debugApp(app, 'registerSubdomains: Upsert error. Will retry.', error.message); - return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain })); // try again - } - - retryCallback(null, error ? new BoxError(BoxError.EXTERNAL_ERROR, error.message, domain) : null); - }); - }); - }, function (error, result) { - if (error || result) return iteratorDone(error || result); - - iteratorDone(null); - }); - }, callback); - }); -} - -function unregisterSubdomains(app, allDomains, callback) { - assert.strictEqual(typeof app, 'object'); - assert(Array.isArray(allDomains)); - assert.strictEqual(typeof callback, 'function'); - - sysinfo.getServerIp(function (error, ip) { - if (error) return callback(error); - - async.eachSeries(allDomains, function (domain, iteratorDone) { - async.retry({ times: 30, interval: 5000 }, function (retryCallback) { - debugApp(app, 'Unregistering subdomain: %s%s', domain.subdomain ? (domain.subdomain + '.') : '', domain.domain); - - domains.removeDnsRecords(domain.subdomain, domain.domain, 'A', [ ip ], function (error) { - if (error && error.reason === BoxError.NOT_FOUND) return retryCallback(null, null); - if (error && (error.reason === BoxError.SBUSY || error.reason === BoxError.EXTERNAL_ERROR)) { - debugApp(app, 'registerSubdomains: Remove error. Will retry.', error.message); - return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain })); // try again - } - - retryCallback(null, error ? new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain }) : null); - }); - }, function (error, result) { - if (error || result) return iteratorDone(error || result); - - iteratorDone(); - }); - }, callback); - }); -} - function waitForDnsPropagation(app, callback) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof callback, 'function'); @@ -539,7 +461,8 @@ function install(app, args, progressCallback, callback) { async.series([ progressCallback.bind(null, { percent: 30, message: 'Registering subdomains' }), - registerSubdomains.bind(null, app, overwriteDns) + + domains.registerLocations.bind(null, [ { subdomain: app.location, domain: app.domain }].concat(app.alternateDomains).concat(app.aliasDomains), { overwriteDns }) ], done); }, @@ -687,7 +610,7 @@ function changeLocation(app, args, progressCallback, callback) { if (obsoleteDomains.length === 0) return next(); - unregisterSubdomains(app, obsoleteDomains, next); + domains.unregisterLocations(obsoleteDomains, next); }, function setupDnsIfNeeded(done) { @@ -695,7 +618,7 @@ function changeLocation(app, args, progressCallback, callback) { async.series([ progressCallback.bind(null, { percent: 30, message: 'Registering subdomains' }), - registerSubdomains.bind(null, app, overwriteDns) + domains.registerLocations.bind(null, [ { subdomain: app.location, domain: app.domain }].concat(app.alternateDomains).concat(app.aliasDomains), { overwriteDns }) ], done); }, @@ -1029,7 +952,7 @@ function uninstall(app, args, progressCallback, callback) { docker.deleteImage.bind(null, app.manifest), progressCallback.bind(null, { percent: 70, message: 'Unregistering domains' }), - unregisterSubdomains.bind(null, app, [ { subdomain: app.location, domain: app.domain } ].concat(app.alternateDomains).concat(app.aliasDomains)), + domains.unregisterLocations.bind(null, [ { subdomain: app.location, domain: app.domain } ].concat(app.alternateDomains).concat(app.aliasDomains)), progressCallback.bind(null, { percent: 80, message: 'Cleanup icon' }), removeIcon.bind(null, app), diff --git a/src/domains.js b/src/domains.js index 699305fd3..0456b9815 100644 --- a/src/domains.js +++ b/src/domains.js @@ -26,10 +26,14 @@ module.exports = exports = { parentDomain, + registerLocations, + unregisterLocations, + checkDnsRecords }; var assert = require('assert'), + async = require('async'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), crypto = require('crypto'), @@ -342,6 +346,7 @@ function clear(callback) { } // returns the 'name' that needs to be inserted into zone +// eslint-disable-next-line no-unused-vars function getName(domain, location, type) { const part = domain.domain.slice(0, -domain.zoneName.length - 1); @@ -468,3 +473,78 @@ function makeWildcard(vhost) { parts[0] = '*'; return parts.join('.'); } + +function registerLocations(locations, options, callback) { + assert(Array.isArray(locations)); + assert.strictEqual(typeof options, 'object'); + assert.strictEqual(typeof callback, 'function'); + + debug(`registerLocations: Will register ${JSON.stringify(locations)}`); + + const overwrite = options.overwrite || false; + + sysinfo.getServerIp(function (error, ip) { + if (error) return callback(error); + + async.eachSeries(locations, function (location, iteratorDone) { + async.retry({ times: 200, interval: 5000 }, function (retryCallback) { + debug('Registering location: %s%s', location.subdomain ? (location.subdomain + '.') : '', location.domain); + + // get the current record before updating it + getDnsRecords(location.subdomain, location.domain, 'A', function (error, values) { + if (error && error.reason === BoxError.EXTERNAL_ERROR) return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location })); // try again + if (error && error.reason === BoxError.ACCESS_DENIED) return retryCallback(null, new BoxError(BoxError.ACCESS_DENIED, error.message, { domain: location })); + if (error && error.reason === BoxError.NOT_FOUND) return retryCallback(null, new BoxError(BoxError.NOT_FOUND, error.message, { domain: location })); + if (error) return retryCallback(null, new BoxError(BoxError.EXTERNAL_ERROR, error.message, location)); // give up for other errors + + if (values.length !== 0 && values[0] === ip) return retryCallback(null); // up-to-date + + // refuse to update any existing DNS record for custom domains that we did not create + if (values.length !== 0 && !overwrite) return retryCallback(null, new BoxError(BoxError.ALREADY_EXISTS, 'DNS Record already exists', { domain: location })); + + upsertDnsRecords(location.subdomain, location.domain, 'A', [ ip ], function (error) { + if (error && (error.reason === BoxError.BUSY || error.reason === BoxError.EXTERNAL_ERROR)) { + debug('registerSubdomains: Upsert error. Will retry.', error.message); + return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location })); // try again + } + + retryCallback(null, error ? new BoxError(BoxError.EXTERNAL_ERROR, error.message, location) : null); + }); + }); + }, function (error, result) { + if (error || result) return iteratorDone(error || result); + + iteratorDone(null); + }); + }, callback); + }); +} + +function unregisterLocations(locations, callback) { + assert(Array.isArray(locations)); + assert.strictEqual(typeof callback, 'function'); + + sysinfo.getServerIp(function (error, ip) { + if (error) return callback(error); + + async.eachSeries(locations, function (location, iteratorDone) { + async.retry({ times: 30, interval: 5000 }, function (retryCallback) { + debug('Unregistering location: %s%s', location.subdomain ? (location.subdomain + '.') : '', location.domain); + + removeDnsRecords(location.subdomain, location.domain, 'A', [ ip ], function (error) { + if (error && error.reason === BoxError.NOT_FOUND) return retryCallback(null, null); + if (error && (error.reason === BoxError.SBUSY || error.reason === BoxError.EXTERNAL_ERROR)) { + debug('unregisterLocations: Remove error. Will retry.', error.message); + return retryCallback(new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location })); // try again + } + + retryCallback(null, error ? new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location }) : null); + }); + }, function (error, result) { + if (error || result) return iteratorDone(error || result); + + iteratorDone(); + }); + }, callback); + }); +} diff --git a/src/test/apptask-test.js b/src/test/apptask-test.js index 9c66df5c8..aaa17430d 100644 --- a/src/test/apptask-test.js +++ b/src/test/apptask-test.js @@ -227,7 +227,7 @@ describe('apptask', function () { .post('/2013-04-01/hostedzone/ZONEID/rrset/') .reply(200, js2xml('ChangeResourceRecordSetsResponse', { ChangeInfo: { Id: 'RRID', Status: 'INSYNC' } })); - apptask._registerSubdomains(APP, true /* overwrite */, function (error) { + apptask._registerLocations(APP, true /* overwrite */, function (error) { expect(error).to.be(null); expect(awsScope.isDone()).to.be.ok(); done();