diff --git a/src/dns.js b/src/dns.js index 8ac13e0a7..e1f82f025 100644 --- a/src/dns.js +++ b/src/dns.js @@ -195,6 +195,30 @@ function makeWildcard(vhost) { return parts.join('.'); } +async function registerLocation(location, options, recordType, recordValue) { + const overwriteDns = options.overwriteDns || false; + + // get the current record before updating it + const [getError, values] = await safe(getDnsRecords(location.subdomain, location.domain, recordType)); + if (getError) { + const retryable = getError.reason !== BoxError.ACCESS_DENIED && getError.reason !== BoxError.NOT_FOUND; + debug(`registerLocation: Get error. retryable: ${retryable}. ${upsertError.message}`); + throw new BoxError(getError.reason, getError.message, { domain: location, retryable }); + } + + if (values.length === 1 && values[0] === recordValue) return; // up-to-date + + // refuse to update any existing DNS record for custom domains that we did not create + if (values.length !== 0 && !overwriteDns) throw new BoxError(BoxError.ALREADY_EXISTS, `DNS ${recordType} record already exists`, { domain: location, retryable: false }); + + const [upsertError] = await safe(upsertDnsRecords(location.subdomain, location.domain, recordType, [ recordValue ])); + if (upsertError) { + const retryable = upsertError.reason === BoxError.BUSY || upsertError.reason === BoxError.EXTERNAL_ERROR; + debug(`registerLocation: Upsert error. retryable: ${retryable}. ${upsertError.message}`); + throw new BoxError(BoxError.EXTERNAL_ERROR, upsertError.message, { domain: location, retryable }); + } +} + async function registerLocations(locations, options, progressCallback) { assert(Array.isArray(locations)); assert.strictEqual(typeof options, 'object'); @@ -202,61 +226,39 @@ async function registerLocations(locations, options, progressCallback) { debug(`registerLocations: Will register ${JSON.stringify(locations)} with options ${JSON.stringify(options)}`); - const overwriteDns = options.overwriteDns || false; - - const ip = await sysinfo.getServerIPv4(); + const ipv4 = await sysinfo.getServerIPv4(); for (const location of locations) { - const error = await promiseRetry({ times: 200, interval: 5000, debug }, async function () { // returns error to abort the "retry" - progressCallback({ message: `Registering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` }); + progressCallback({ message: `Registering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` }); - // get the current record before updating it - const [error, values] = await safe(getDnsRecords(location.subdomain, location.domain, 'A')); - if (error && error.reason === BoxError.EXTERNAL_ERROR) throw new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location }); // try again - // give up for other errors - if (error && error.reason === BoxError.ACCESS_DENIED) return new BoxError(BoxError.ACCESS_DENIED, error.message, { domain: location }); - if (error && error.reason === BoxError.NOT_FOUND) return new BoxError(BoxError.NOT_FOUND, error.message, { domain: location }); - if (error) return new BoxError(BoxError.EXTERNAL_ERROR, error.message, location); - - if (values.length !== 0 && values[0] === ip) return null; // up-to-date - - // refuse to update any existing DNS record for custom domains that we did not create - if (values.length !== 0 && !overwriteDns) return new BoxError(BoxError.ALREADY_EXISTS, 'DNS Record already exists', { domain: location }); - - const [upsertError] = await safe(upsertDnsRecords(location.subdomain, location.domain, 'A', [ ip ])); - if (upsertError && (upsertError.reason === BoxError.BUSY || upsertError.reason === BoxError.EXTERNAL_ERROR)) { - progressCallback({ message: `registerSubdomains: Upsert error. Will retry. ${upsertError.message}` }); - throw new BoxError(BoxError.EXTERNAL_ERROR, upsertError.message, { domain: location }); // try again - } - - return upsertError ? new BoxError(BoxError.EXTERNAL_ERROR, upsertError.message, location) : null; + await promiseRetry({ times: 200, interval: 5000, debug, retry: (error) => error.retryable }, async function () { + await registerLocation(location, options, 'A', ipv4); }); - - if (error) throw error; } } +async function unregisterLocation(location, recordType, recordValue) { + const [error] = await safe(removeDnsRecords(location.subdomain, location.domain, recordType, [ recordValue ])); + if (!error || error.reason === BoxError.NOT_FOUND) return; + + const retryable = error.reason === BoxError.BUSY || error.reason === BoxError.EXTERNAL_ERROR; + debug(`unregisterLocation: Error unregistering location ${recordType}. retryable: ${retryable}. ${error.message}`); + + throw new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location, retryable }); +} + async function unregisterLocations(locations, progressCallback) { assert(Array.isArray(locations)); assert.strictEqual(typeof progressCallback, 'function'); - const ip = await sysinfo.getServerIPv4(); + const ipv4 = await sysinfo.getServerIPv4(); for (const location of locations) { - const error = await promiseRetry({ times: 30, interval: 5000, debug }, async function () { // returns error to abort the "retry" - progressCallback({ message: `Unregistering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` }); + progressCallback({ message: `Unregistering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` }); - const [error] = await safe(removeDnsRecords(location.subdomain, location.domain, 'A', [ ip ])); - if (error && error.reason === BoxError.NOT_FOUND) return; - if (error && (error.reason === BoxError.BUSY || error.reason === BoxError.EXTERNAL_ERROR)) { - progressCallback({ message: `Error unregistering location. Will retry. ${error.message}`}); - throw new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location }); // try again - } - - return error ? new BoxError(BoxError.EXTERNAL_ERROR, error.message, { domain: location }) : null; // give up for other errors + await promiseRetry({ times: 30, interval: 5000, debug, retry: (error) => error.retryable }, async function () { + await unregisterLocation(location, 'A', ipv4); }); - - if (error) throw error; } }