diff --git a/src/addons.js b/src/addons.js index 0854e1c2b..23f9fe807 100644 --- a/src/addons.js +++ b/src/addons.js @@ -67,6 +67,13 @@ const RMADDONDIR_CMD = path.join(__dirname, 'scripts/rmaddondir.sh'); // setup can be called multiple times for the same app (configure crash restart) and existing data must not be lost // teardown is destructive. app data stored with the addon is lost var KNOWN_ADDONS = { + turn: { + setup: NOOP, + teardown: NOOP, + backup: NOOP, + restore: NOOP, + clear: NOOP + }, email: { setup: setupEmail, teardown: teardownEmail, @@ -154,6 +161,11 @@ var KNOWN_ADDONS = { }; const KNOWN_SERVICES = { + turn: { + status: statusTurn, + restart: restartContainer.bind(null, 'turn'), + defaultMemoryLimit: 256 * 1024 * 1024 + }, mail: { status: containerStatus.bind(null, 'mail', 'CLOUDRON_MAIL_TOKEN'), restart: mail.restartMail, @@ -237,6 +249,7 @@ function rebuildService(serviceName, callback) { // this attempts to recreate the service docker container if they don't exist but platform infra version is unchanged // passing an infra version of 'none' will not attempt to purge existing data, not sure if this is good or bad + if (serviceName === 'turn') return startTurn({ version: 'none' }, callback); if (serviceName === 'mongodb') return startMongodb({ version: 'none' }, callback); if (serviceName === 'postgresql') return startPostgresql({ version: 'none' }, callback); if (serviceName === 'mysql') return startMysql({ version: 'none' }, callback); @@ -650,6 +663,7 @@ function startServices(existingInfra, callback) { if (existingInfra.version !== infra.version) { debug(`startServices: ${existingInfra.version} -> ${infra.version}. starting all services`); startFuncs.push( + startTurn.bind(null, existingInfra), startMysql.bind(null, existingInfra), startPostgresql.bind(null, existingInfra), startMongodb.bind(null, existingInfra), @@ -658,6 +672,7 @@ function startServices(existingInfra, callback) { } else { assert.strictEqual(typeof existingInfra.images, 'object'); + if (!existingInfra.images.turn || infra.images.turn.tag !== existingInfra.images.turn.tag) startFuncs.push(startTurn.bind(null, existingInfra)); if (infra.images.mysql.tag !== existingInfra.images.mysql.tag) startFuncs.push(startMysql.bind(null, existingInfra)); if (infra.images.postgresql.tag !== existingInfra.images.postgresql.tag) startFuncs.push(startPostgresql.bind(null, existingInfra)); if (infra.images.mongodb.tag !== existingInfra.images.mongodb.tag) startFuncs.push(startMongodb.bind(null, existingInfra)); @@ -1347,6 +1362,35 @@ function restorePostgreSql(app, options, callback) { }); } +function startTurn(existingInfra, callback) { + assert.strictEqual(typeof existingInfra, 'object'); + assert.strictEqual(typeof callback, 'function'); + + const tag = infra.images.turn.tag; + const memoryLimit = 256; + + if (existingInfra.version === infra.version && existingInfra.images.turn && infra.images.turn.tag === existingInfra.images.turn.tag) return callback(); + + const cmd = `docker run --restart=always -d --name="turn" \ + --hostname turn \ + --net host \ + --log-driver syslog \ + --log-opt syslog-address=udp://127.0.0.1:2514 \ + --log-opt syslog-format=rfc5424 \ + --log-opt tag=turn \ + -m ${memoryLimit}m \ + --memory-swap ${memoryLimit * 2}m \ + --dns 172.18.0.1 \ + --dns-search=. \ + -p 222:22 \ + -v "${paths.APPS_DATA_DIR}:/app/data" \ + -v "/etc/ssh:/etc/ssh:ro" \ + --label isCloudronManaged=true \ + --read-only -v /tmp -v /run "${tag}"`; + + shell.exec('startTurn', cmd, callback); +} + function startMongodb(existingInfra, callback) { assert.strictEqual(typeof existingInfra, 'object'); assert.strictEqual(typeof callback, 'function'); @@ -1706,6 +1750,27 @@ function restoreRedis(app, options, callback) { }); } +function statusTurn(callback) { + assert.strictEqual(typeof callback, 'function'); + + docker.inspect('turn', function (error, container) { + if (error && error.reason === BoxError.NOT_FOUND) return callback(null, { status: exports.SERVICE_STATUS_STOPPED }); + if (error) return callback(error); + + docker.memoryUsage(container.Id, function (error, result) { + if (error) return callback(error); + + var tmp = { + status: container.State.Running ? exports.SERVICE_STATUS_ACTIVE : exports.SERVICE_STATUS_STOPPED, + memoryUsed: result.memory_stats.usage, + memoryPercent: parseInt(100 * result.memory_stats.usage / result.memory_stats.limit) + }; + + callback(null, tmp); + }); + }); +} + function statusDocker(callback) { assert.strictEqual(typeof callback, 'function'); diff --git a/src/infra_version.js b/src/infra_version.js index 6adfd282a..335bf88de 100644 --- a/src/infra_version.js +++ b/src/infra_version.js @@ -15,6 +15,7 @@ exports = module.exports = { // a major version bump in the db containers will trigger the restore logic that uses the db dumps // docker inspect --format='{{index .RepoDigests 0}}' $IMAGE to get the sha256 'images': { + 'turn': { repo: 'cloudron/coturn', tag: 'cloudron/coturn:0.0.1@sha256:a069b9f5788efb16eca797db6ae82f3d0e35c60273c052e573b3cb56105fd2ab' }, 'mysql': { repo: 'cloudron/mysql', tag: 'cloudron/mysql:2.1.0@sha256:eee0dfd3829d563f2063084bc0d7c8802c4bdd6e233159c6226a17ff7a9a3503' }, 'postgresql': { repo: 'cloudron/postgresql', tag: 'cloudron/postgresql:2.0.2@sha256:6dcee0731dfb9b013ed94d56205eee219040ee806c7e251db3b3886eaa4947ff' }, 'mongodb': { repo: 'cloudron/mongodb', tag: 'cloudron/mongodb:2.1.0@sha256:6d1bf221cfe6124957e2c58b57c0a47214353496009296acb16adf56df1da9d5' },