diff --git a/migrations/schema.sql b/migrations/schema.sql index 7c330fafd..4fedc9746 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -60,7 +60,7 @@ CREATE TABLE IF NOT EXISTS apps( manifestJson TEXT, httpPort INTEGER, // this is the nginx proxy port and not manifest.httpPort location VARCHAR(128) NOT NULL UNIQUE, - dnsRecordId VARCHAR(512), // tracks any id that we got back to track dns updates (unused) + dnsRecordId VARCHAR(512), // tracks any id that we got back to track dns updates accessRestrictionJson TEXT, // { users: [ ], groups: [ ] } createdAt TIMESTAMP(2) NOT NULL DEFAULT CURRENT_TIMESTAMP, memoryLimit BIGINT DEFAULT 0, diff --git a/src/apptask.js b/src/apptask.js index 180bbf4a3..16c8a0a42 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -273,6 +273,7 @@ function registerSubdomain(app, overwrite, callback) { }, function (error, result) { if (error || result instanceof Error) return callback(error || result); + // dnsRecordId tracks whether we created this DNS record so that we can unregister later updateApp(app, { dnsRecordId: result }, callback); }); }); @@ -289,6 +290,11 @@ function unregisterSubdomain(app, location, callback) { return callback(null); } + if (!app.dnsRecordId) { + debugApp(app, 'Skip unregister of record not created by cloudron'); + return callback(null); + } + sysinfo.getPublicIp(function (error, ip) { if (error) return callback(error); @@ -498,6 +504,9 @@ function configure(app, callback) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof callback, 'function'); + // oldConfig can be null during an infra update + var locationChanged = app.oldConfig && app.oldConfig.location !== app.location; + async.series([ updateApp.bind(null, app, { installationProgress: '10, Cleaning up old install' }), unconfigureNginx.bind(null, app), @@ -506,8 +515,7 @@ function configure(app, callback) { stopApp.bind(null, app), deleteContainers.bind(null, app), function (next) { - // oldConfig can be null during an infra update - if (!app.oldConfig || app.oldConfig.location === app.location) return next(); + if (!locationChanged) return next(); unregisterSubdomain(app, app.oldConfig.location, next); }, @@ -517,7 +525,7 @@ function configure(app, callback) { downloadIcon.bind(null, app), updateApp.bind(null, app, { installationProgress: '35, Registering subdomain' }), - registerSubdomain.bind(null, app, true /* overwrite */), + registerSubdomain.bind(null, app, !locationChanged /* overwrite */), // if location changed, do not overwrite to detect conflicts updateApp.bind(null, app, { installationProgress: '40, Downloading image' }), docker.downloadImage.bind(null, app.manifest), diff --git a/src/dns/digitalocean.js b/src/dns/digitalocean.js index 6ff14ab55..442f46e9e 100644 --- a/src/dns/digitalocean.js +++ b/src/dns/digitalocean.js @@ -13,6 +13,7 @@ var assert = require('assert'), constants = require('../constants.js'), debug = require('debug')('box:dns/digitalocean'), dns = require('dns'), + safe = require('safetydance'), SubdomainError = require('../subdomains.js').SubdomainError, superagent = require('superagent'), util = require('util'); @@ -77,7 +78,7 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { if (error) return callback(error); // used to track available records to update instead of create - var i = 0; + var i = 0, recordIds = []; async.eachSeries(values, function (value, callback) { var priority = null; @@ -106,6 +107,8 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { if (result.statusCode === 422) return callback(new SubdomainError(SubdomainError.BAD_FIELD, result.body.message)); if (result.statusCode !== 201) return callback(new SubdomainError(SubdomainError.EXTERNAL_ERROR, formatError(result))); + recordIds.push(safe.query(result.body, 'domain_record.id')); + return callback(null); }); } else { @@ -122,13 +125,15 @@ function upsert(dnsConfig, zoneName, subdomain, type, values, callback) { if (result.statusCode === 422) return callback(new SubdomainError(SubdomainError.BAD_FIELD, result.body.message)); if (result.statusCode !== 200) return callback(new SubdomainError(SubdomainError.EXTERNAL_ERROR, formatError(result))); + recordIds.push(safe.query(result.body, 'domain_record.id')); + return callback(null); }); } - }, function (error) { + }, function (error, id) { if (error) return callback(error); - callback(null, 'unused'); + callback(null, '' + recordIds[0]); // DO ids are integers }); }); } diff --git a/src/test/dns-test.js b/src/test/dns-test.js index a320c8c1a..1ac1e6319 100644 --- a/src/test/dns-test.js +++ b/src/test/dns-test.js @@ -109,7 +109,7 @@ describe('dns provider', function () { subdomains.upsert('test', 'A', [ '1.2.3.4' ], function (error, result) { expect(error).to.eql(null); - expect(result).to.eql('unused'); + expect(result).to.eql('3352892'); expect(req1.isDone()).to.be.ok(); expect(req2.isDone()).to.be.ok(); @@ -155,11 +155,11 @@ describe('dns provider', function () { .reply(200, { domain_records: [ DOMAIN_RECORD_0, DOMAIN_RECORD_1 ] }); var req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; }) .put('/v2/domains/' + config.zoneName() + '/records/' + DOMAIN_RECORD_1.id) - .reply(200, { domain_records: DOMAIN_RECORD_1_NEW }); + .reply(200, { domain_record: DOMAIN_RECORD_1_NEW }); subdomains.upsert('test', 'A', [ DOMAIN_RECORD_1_NEW.data ], function (error, result) { expect(error).to.eql(null); - expect(result).to.eql('unused'); + expect(result).to.eql('3352893'); expect(req1.isDone()).to.be.ok(); expect(req2.isDone()).to.be.ok(); @@ -235,17 +235,17 @@ describe('dns provider', function () { .reply(200, { domain_records: [ DOMAIN_RECORD_0, DOMAIN_RECORD_1, DOMAIN_RECORD_2 ] }); var req2 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; }) .put('/v2/domains/' + config.zoneName() + '/records/' + DOMAIN_RECORD_1.id) - .reply(200, { domain_records: DOMAIN_RECORD_1_NEW }); + .reply(200, { domain_record: DOMAIN_RECORD_1_NEW }); var req3 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; }) .put('/v2/domains/' + config.zoneName() + '/records/' + DOMAIN_RECORD_2.id) - .reply(200, { domain_records: DOMAIN_RECORD_2_NEW }); + .reply(200, { domain_record: DOMAIN_RECORD_2_NEW }); var req4 = nock(DIGITALOCEAN_ENDPOINT).filteringRequestBody(function () { return false; }) .post('/v2/domains/' + config.zoneName() + '/records') - .reply(201, { domain_records: DOMAIN_RECORD_2_NEW }); + .reply(201, { domain_record: DOMAIN_RECORD_2_NEW }); subdomains.upsert('', 'TXT', [ DOMAIN_RECORD_2_NEW.data, DOMAIN_RECORD_1_NEW.data, DOMAIN_RECORD_3_NEW.data ], function (error, result) { expect(error).to.eql(null); - expect(result).to.eql('unused'); + expect(result).to.eql('3352893'); expect(req1.isDone()).to.be.ok(); expect(req2.isDone()).to.be.ok(); expect(req3.isDone()).to.be.ok();