diff --git a/src/addons.js b/src/addons.js index 5bad76dd3..feec0a9d4 100644 --- a/src/addons.js +++ b/src/addons.js @@ -642,28 +642,36 @@ function setupPostgreSql(app, options, callback) { debugApp(app, 'Setting up postgresql'); - appdb.getAddonConfigByName(app.id, 'postgresql', 'POSTGRESQL_PASSWORD', function (error, existingPassword) { + const appId = app.id.replace(/-/g, ''); + + appdb.getAddonConfigByName(appId, 'postgresql', 'POSTGRESQL_PASSWORD', function (error, existingPassword) { if (error && error.reason !== DatabaseError.NOT_FOUND) return callback(error); - const password = error ? hat(4 * 128) : existingPassword; - const appId = app.id.replace(/-/g, ''); + const data = { + database: `db${appId}`, + username: `user${appId}`, + password: error ? hat(4 * 128) : existingPassword + }; - var cmd = [ '/addons/postgresql/service.sh', 'add', appId, password ]; - - docker.execContainer('postgresql', cmd, { bufferStdout: true }, function (error) { + getAddonDetails('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN', function (error, result) { if (error) return callback(error); - var env = [ - { name: 'POSTGRESQL_URL', value: `postgres://user${appId}:${password}@postgresql/db${appId}` }, - { name: 'POSTGRESQL_USERNAME', value: `user${appId}` }, - { name: 'POSTGRESQL_PASSWORD', value: password }, - { name: 'POSTGRESQL_HOST', value: 'postgresql' }, - { name: 'POSTGRESQL_PORT', value: '5432' }, - { name: 'POSTGRESQL_DATABASE', value: `db${appId}` } - ]; + request.post(`https://${result.ip}:3000/databases?access_token=${result.token}`, { rejectUnauthorized: false, json: data }, function (error, response, body) { + if (error) return callback(new Error('Error setting up postgresql: ' + error)); + if (response.statusCode !== 201) return callback(new Error(`Error setting up postgresql. Status code: ${response.statusCode}`)); - debugApp(app, 'Setting postgresql addon config to %j', env); - appdb.setAddonConfig(app.id, 'postgresql', env, callback); + var env = [ + { name: 'POSTGRESQL_URL', value: `postgres://${data.username}:${data.password}@postgresql/${data.database}` }, + { name: 'POSTGRESQL_USERNAME', value: data.username }, + { name: 'POSTGRESQL_PASSWORD', value: data.password }, + { name: 'POSTGRESQL_HOST', value: 'postgresql' }, + { name: 'POSTGRESQL_PORT', value: '5432' }, + { name: 'POSTGRESQL_DATABASE', value: data.database } + ]; + + debugApp(app, 'Setting postgresql addon config to %j', env); + appdb.setAddonConfig(app.id, 'postgresql', env, callback); + }); }); }); } @@ -675,14 +683,17 @@ function clearPostgreSql(app, options, callback) { const appId = app.id.replace(/-/g, ''); - var cmd = [ '/addons/postgresql/service.sh', 'clear', appId ]; - debugApp(app, 'Clearing postgresql'); - docker.execContainer('postgresql', cmd, { }, function (error) { + getAddonDetails('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN', function (error, result) { if (error) return callback(error); - callback(); + request.delete(`https://${result.ip}:3000/databases/db${appId}/clear?access_token=${result.token}}`, { rejectUnauthorized: false }, function (error, response, body) { + if (error) return callback(new Error('Error clearing postgresql: ' + error)); + if (response.statusCode !== 200) return callback(new Error(`Error clearing postgresql. Status code: ${response.statusCode}`)); + + callback(null); + }); }); } @@ -693,14 +704,15 @@ function teardownPostgreSql(app, options, callback) { const appId = app.id.replace(/-/g, ''); - var cmd = [ '/addons/postgresql/service.sh', 'remove', appId ]; - - debugApp(app, 'Tearing down postgresql'); - - docker.execContainer('postgresql', cmd, { }, function (error) { + getAddonDetails('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN', function (error, result) { if (error) return callback(error); - appdb.unsetAddonConfig(app.id, 'postgresql', callback); + request.delete(`https://${result.ip}:3000/databases/db${appId}?access_token=${result.token}&username=user${appId}`, { rejectUnauthorized: false }, function (error, response, body) { + if (error) return callback(new Error('Error tearing down postgresql: ' + error)); + if (response.statusCode !== 200) return callback(new Error(`Error tearing down postgresql. Status code: ${response.statusCode}`)); + + appdb.unsetAddonConfig(app.id, 'postgresql', callback); + }); }); } @@ -711,15 +723,22 @@ function backupPostgreSql(app, options, callback) { debugApp(app, 'Backing up postgresql'); - callback = once(callback); // ChildProcess exit may or may not be called after error + callback = once(callback); // protect from multiple returns with streams - var output = fs.createWriteStream(path.join(paths.APPS_DATA_DIR, app.id, 'postgresqldump')); - output.on('error', callback); + getAddonDetails('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN', function (error, result) { + if (error) return callback(error); - const appId = app.id.replace(/-/g, ''); - var cmd = [ '/addons/postgresql/service.sh', 'backup', appId ]; + const writeStream = fs.createWriteStream(path.join(paths.APPS_DATA_DIR, app.id, 'postgresqldump')); + writeStream.on('error', callback); - docker.execContainer('postgresql', cmd, { stdout: output }, callback); + const req = request.post(`https://${result.ip}:3000/databases/${app.id}/backup?access_token=${result.token}`, { rejectUnauthorized: false }, function (error, response, body) { + if (error) return callback(error); + if (response.statusCode !== 200) return callback(new Error(`Unexpected response from mongodb addon ${response.statusCode}`)); + + callback(null); + }); + req.pipe(writeStream); + }); } function restorePostgreSql(app, options, callback) { @@ -727,17 +746,31 @@ function restorePostgreSql(app, options, callback) { assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof callback, 'function'); - callback = once(callback); - - debugApp(app, 'restorePostgreSql'); - - var input = fs.createReadStream(path.join(paths.APPS_DATA_DIR, app.id, 'postgresqldump')); - input.on('error', callback); + debugApp(app, 'Restore postgresql'); const appId = app.id.replace(/-/g, ''); - var cmd = [ '/addons/postgresql/service.sh', 'restore', appId ]; - docker.execContainer('postgresql', cmd, { stdin: input }, callback); + callback = once(callback); // protect from multiple returns with streams + + setupPostgreSql(app, options, function (error) { + if (error) return callback(error); + + getAddonDetails('postgresql', 'CLOUDRON_POSTGRESQL_TOKEN', function (error, result) { + if (error) return callback(error); + + var input = fs.createReadStream(path.join(paths.APPS_DATA_DIR, app.id, 'postgresqldump')); + input.on('error', callback); + + const restoreReq = request.post(`https://${result.ip}:3000/databases/${app.id}/restore?access_token=${result.token}&username=user${appId}`, { rejectUnauthorized: false }, function (error, response, body) { + if (error) return callback(error); + if (response.statusCode !== 200) return callback(new Error(`Unexpected response from postgresql addon ${response.statusCode}`)); + + callback(null); + }); + + input.pipe(restoreReq); + }); + }); } function getAddonDetails(containerName, tokenEnvName, callback) { diff --git a/src/infra_version.js b/src/infra_version.js index 7d60f168a..c2fcee1c9 100644 --- a/src/infra_version.js +++ b/src/infra_version.js @@ -16,7 +16,7 @@ exports = module.exports = { // docker inspect --format='{{index .RepoDigests 0}}' $IMAGE to get the sha256 'images': { 'mysql': { repo: 'cloudron/mysql', tag: 'cloudron/mysql:1.1.0@sha256:0459023f16e65985e8d74b490b5c4f38c9d1b7a4e5ec8049c08256d42decf00e' }, - 'postgresql': { repo: 'cloudron/postgresql', tag: 'cloudron/postgresql:1.1.0@sha256:731d802211fa08ab951ebac2565048d44526c73948245f18df0ffc27929a8a08' }, + 'postgresql': { repo: 'cloudron/postgresql', tag: 'cloudron/postgresql:2.0.0@sha256:520720bc1caabeb6b0c852635bc24b50ccf7f0d74dc715fdbafbc40622e86b5a' }, 'mongodb': { repo: 'cloudron/mongodb', tag: 'cloudron/mongodb:2.0.1@sha256:931267c9243d23c5b41118f29f011f529ca9865db10a5c1c26157eed9efaa676' }, 'redis': { repo: 'cloudron/redis', tag: 'cloudron/redis:2.0.1@sha256:73d300c87d0fd1b8f01157fd69c65aa04377498bc7daa7aafd6d6fdcf2e5a35e' }, 'mail': { repo: 'cloudron/mail', tag: 'cloudron/mail:1.4.0@sha256:28e65b446569a324f4b28e920d43ac9723f9aa9699a629bec7368a2a74669f88' }, diff --git a/src/platform.js b/src/platform.js index ad1fe2e96..0bbf6990e 100644 --- a/src/platform.js +++ b/src/platform.js @@ -210,12 +210,9 @@ function startPostgresql(callback) { const tag = infra.images.postgresql.tag; const dataDir = paths.PLATFORM_DATA_DIR; const rootPassword = hat(8 * 128); + const cloudronToken = hat(8 * 128); const memoryLimit = (1 + Math.round(os.totalmem()/(1024*1024*1024)/4)) * 256; - if (!safe.fs.writeFileSync(paths.ADDON_CONFIG_DIR + '/postgresql_vars.sh', 'POSTGRESQL_ROOT_PASSWORD=' + rootPassword, 'utf8')) { - return callback(new Error('Could not create postgresql var file:' + safe.error.message)); - } - const cmd = `docker run --restart=always -d --name="postgresql" \ --net cloudron \ --net-alias postgresql \ @@ -227,8 +224,9 @@ function startPostgresql(callback) { --memory-swap ${memoryLimit * 2}m \ --dns 172.18.0.1 \ --dns-search=. \ + -e POSTGRESQL_ROOT_PASSWORD="${rootPassword}" \ + -e CLOUDRON_POSTGRESQL_TOKEN="${cloudronToken}" \ -v "${dataDir}/postgresql:/var/lib/postgresql" \ - -v "${dataDir}/addons/postgresql_vars.sh:/etc/postgresql/postgresql_vars.sh:ro" \ --read-only -v /tmp -v /run "${tag}"`; shell.execSync('startPostgresql', cmd);