2015-07-20 00:09:47 -07:00
'use strict' ;
exports = module . exports = {
2018-10-16 14:07:41 -07:00
startAddons : startAddons ,
updateAddonConfig : updateAddonConfig ,
2015-07-20 00:09:47 -07:00
setupAddons : setupAddons ,
teardownAddons : teardownAddons ,
backupAddons : backupAddons ,
restoreAddons : restoreAddons ,
2018-09-15 17:05:04 -07:00
clearAddons : clearAddons ,
2015-07-20 00:09:47 -07:00
getEnvironment : getEnvironment ,
2018-09-13 13:55:49 -07:00
getMountsSync : getMountsSync ,
2015-11-02 11:20:50 -08:00
getContainerNamesSync : getContainerNamesSync ,
2015-07-20 00:09:47 -07:00
// exported for testing
2015-10-07 15:55:57 -07:00
_setupOauth : setupOauth ,
_teardownOauth : teardownOauth
2015-07-20 00:09:47 -07:00
} ;
2018-04-26 15:54:53 -07:00
var accesscontrol = require ( './accesscontrol.js' ) ,
appdb = require ( './appdb.js' ) ,
2015-07-20 00:09:47 -07:00
assert = require ( 'assert' ) ,
async = require ( 'async' ) ,
2016-06-03 14:56:45 +02:00
clients = require ( './clients.js' ) ,
2015-07-20 00:09:47 -07:00
config = require ( './config.js' ) ,
2016-06-13 14:43:56 +02:00
ClientsError = clients . ClientsError ,
2018-05-16 19:49:29 -07:00
crypto = require ( 'crypto' ) ,
2018-05-17 19:48:54 -07:00
DatabaseError = require ( './databaseerror.js' ) ,
2015-07-20 00:09:47 -07:00
debug = require ( 'debug' ) ( 'box:addons' ) ,
2016-04-18 10:37:33 -07:00
docker = require ( './docker.js' ) ,
dockerConnection = docker . connection ,
2015-07-20 00:09:47 -07:00
fs = require ( 'fs' ) ,
2018-06-11 12:38:15 -07:00
hat = require ( './hat.js' ) ,
2016-05-24 13:06:59 -07:00
infra = require ( './infra_version.js' ) ,
2018-03-07 20:39:58 -08:00
mail = require ( './mail.js' ) ,
2016-09-23 17:20:32 -07:00
mailboxdb = require ( './mailboxdb.js' ) ,
2015-07-20 00:09:47 -07:00
once = require ( 'once' ) ,
2018-10-16 14:07:41 -07:00
os = require ( 'os' ) ,
2015-07-20 00:09:47 -07:00
path = require ( 'path' ) ,
paths = require ( './paths.js' ) ,
2018-09-18 14:15:23 -07:00
rimraf = require ( 'rimraf' ) ,
2015-07-20 00:09:47 -07:00
safe = require ( 'safetydance' ) ,
2018-10-16 14:07:41 -07:00
semver = require ( 'semver' ) ,
settings = require ( './settings.js' ) ,
2015-07-20 00:09:47 -07:00
shell = require ( './shell.js' ) ,
2018-09-13 12:37:21 +02:00
request = require ( 'request' ) ,
2016-06-03 14:56:45 +02:00
util = require ( 'util' ) ;
2015-07-20 00:09:47 -07:00
2018-10-16 14:07:41 -07:00
const NOOP = function ( app , options , callback ) { return callback ( ) ; } ;
const NOOP _CALLBACK = function ( error ) { if ( error ) debug ( error ) ; } ;
2018-10-15 14:38:33 -07:00
const RMADDON _CMD = path . join ( _ _dirname , 'scripts/rmaddon.sh' ) ;
2015-07-20 00:09:47 -07:00
// 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 = {
2016-05-12 08:54:59 -07:00
email : {
setup : setupEmail ,
teardown : teardownEmail ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : setupEmail ,
clear : NOOP
2016-05-12 08:54:59 -07:00
} ,
2015-07-20 00:09:47 -07:00
ldap : {
setup : setupLdap ,
teardown : teardownLdap ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : setupLdap ,
clear : NOOP
2015-07-20 00:09:47 -07:00
} ,
2015-10-18 09:52:37 -07:00
localstorage : {
2018-09-13 13:55:49 -07:00
setup : setupLocalStorage , // docker creates the directory for us
teardown : teardownLocalStorage ,
2015-10-18 09:52:37 -07:00
backup : NOOP , // no backup because it's already inside app data
2018-09-15 17:05:04 -07:00
restore : NOOP ,
clear : clearLocalStorage
2015-10-18 09:52:37 -07:00
} ,
mongodb : {
setup : setupMongoDb ,
teardown : teardownMongoDb ,
backup : backupMongoDb ,
2018-09-15 17:05:04 -07:00
restore : restoreMongoDb ,
clear : clearMongodb
2015-07-20 00:09:47 -07:00
} ,
mysql : {
setup : setupMySql ,
teardown : teardownMySql ,
backup : backupMySql ,
restore : restoreMySql ,
2018-09-15 17:05:04 -07:00
clear : clearMySql
2015-07-20 00:09:47 -07:00
} ,
2015-10-18 09:52:37 -07:00
oauth : {
setup : setupOauth ,
teardown : teardownOauth ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : setupOauth ,
clear : NOOP
2015-10-18 09:52:37 -07:00
} ,
2015-07-20 00:09:47 -07:00
postgresql : {
setup : setupPostgreSql ,
teardown : teardownPostgreSql ,
backup : backupPostgreSql ,
2018-09-15 17:05:04 -07:00
restore : restorePostgreSql ,
clear : clearPostgreSql
2015-07-20 00:09:47 -07:00
} ,
2016-05-13 14:13:25 -07:00
recvmail : {
setup : setupRecvMail ,
teardown : teardownRecvMail ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : setupRecvMail ,
clear : NOOP
2016-05-13 14:13:25 -07:00
} ,
2015-07-20 00:09:47 -07:00
redis : {
setup : setupRedis ,
teardown : teardownRedis ,
2015-10-12 13:29:27 -07:00
backup : backupRedis ,
2018-09-18 12:28:03 -07:00
restore : restoreRedis ,
2018-09-15 17:05:04 -07:00
clear : clearRedis
2015-07-20 00:09:47 -07:00
} ,
2015-10-18 09:52:37 -07:00
sendmail : {
setup : setupSendMail ,
teardown : teardownSendMail ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : setupSendMail ,
clear : NOOP
2015-10-18 09:52:37 -07:00
} ,
2015-10-18 08:40:24 -07:00
scheduler : {
setup : NOOP ,
teardown : NOOP ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : NOOP ,
clear : NOOP
2018-08-09 11:54:32 +02:00
} ,
2018-08-10 12:31:46 -07:00
docker : {
2018-08-09 11:54:32 +02:00
setup : NOOP ,
teardown : NOOP ,
backup : NOOP ,
2018-09-15 17:05:04 -07:00
restore : NOOP ,
clear : NOOP
2015-07-20 00:09:47 -07:00
}
} ;
function debugApp ( app , args ) {
2018-02-08 15:07:49 +01:00
assert ( typeof app === 'object' ) ;
2015-07-20 00:09:47 -07:00
2018-10-15 14:31:35 -07:00
debug ( ( app . fqdn || app . location ) + ' ' + util . format . apply ( util , Array . prototype . slice . call ( arguments , 1 ) ) ) ;
2015-07-20 00:09:47 -07:00
}
2018-10-16 14:07:41 -07:00
function parseImageTag ( tag ) {
let repository = tag . split ( ':' , 1 ) [ 0 ] ;
let version = tag . substr ( repository . length + 1 ) . split ( '@' , 1 ) [ 0 ] ;
let digest = tag . substr ( repository . length + 1 + version . length + 1 ) . split ( ':' , 2 ) [ 1 ] ;
return { repository , version : semver . parse ( version ) , digest } ;
}
function requiresUpgrade ( existingTag , currentTag ) {
let etag = parseImageTag ( existingTag ) , ctag = parseImageTag ( currentTag ) ;
return etag . version . major !== ctag . version . major ;
}
2018-09-18 19:52:28 +02:00
function getAddonDetails ( containerName , tokenEnvName , callback ) {
assert . strictEqual ( typeof containerName , 'string' ) ;
assert . strictEqual ( typeof tokenEnvName , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
var container = dockerConnection . getContainer ( containerName ) ;
container . inspect ( function ( error , result ) {
if ( error ) return callback ( new Error ( ` Error inspecting ${ containerName } container: ` + error ) ) ;
const ip = safe . query ( result , 'NetworkSettings.Networks.cloudron.IPAddress' , null ) ;
if ( ! ip ) return callback ( new Error ( ` Error getting ${ containerName } container ip ` ) ) ;
// extract the cloudron token for auth
const env = safe . query ( result , 'Config.Env' , null ) ;
if ( ! env ) return callback ( new Error ( ` Error getting ${ containerName } env ` ) ) ;
const tmp = env . find ( function ( e ) { return e . indexOf ( tokenEnvName ) === 0 ; } ) ;
if ( ! tmp ) return callback ( new Error ( ` Error getting ${ containerName } cloudron token env var ` ) ) ;
const token = tmp . slice ( tokenEnvName . length + 1 ) ; // +1 for the = sign
if ( ! token ) return callback ( new Error ( ` Error getting ${ containerName } cloudron token ` ) ) ;
callback ( null , { ip : ip , token : token } ) ;
} ) ;
}
2018-10-14 13:26:01 -07:00
function waitForAddon ( containerName , tokenEnvName , callback ) {
assert . strictEqual ( typeof containerName , 'string' ) ;
assert . strictEqual ( typeof tokenEnvName , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debug ( ` Waiting for ${ containerName } ` ) ;
getAddonDetails ( containerName , tokenEnvName , function ( error , result ) {
if ( error ) return callback ( error ) ;
async . retry ( { times : 10 , interval : 5000 } , function ( retryCallback ) {
request . get ( ` https:// ${ result . ip } :3000/healthcheck?access_token= ${ result . token } ` , { json : true , rejectUnauthorized : false } , function ( error , response ) {
if ( error ) return retryCallback ( new Error ( ` Error waiting for ${ containerName } : ${ error . message } ` ) ) ;
2018-10-16 00:21:15 -07:00
if ( response . statusCode !== 200 || ! response . body . status ) return retryCallback ( new Error ( ` Error waiting for ${ containerName } . Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-10-14 13:26:01 -07:00
retryCallback ( null ) ;
} ) ;
} , callback ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
function setupAddons ( app , addons , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
if ( ! addons ) return callback ( null ) ;
2017-12-21 01:04:38 -08:00
debugApp ( app , 'setupAddons: Setting up %j' , Object . keys ( addons ) ) ;
2015-07-20 11:03:11 -07:00
2015-07-20 00:09:47 -07:00
async . eachSeries ( Object . keys ( addons ) , function iterator ( addon , iteratorCallback ) {
if ( ! ( addon in KNOWN _ADDONS ) ) return iteratorCallback ( new Error ( 'No such addon:' + addon ) ) ;
2015-10-07 16:10:08 -07:00
debugApp ( app , 'Setting up addon %s with options %j' , addon , addons [ addon ] ) ;
2015-07-20 00:09:47 -07:00
2015-10-07 16:10:08 -07:00
KNOWN _ADDONS [ addon ] . setup ( app , addons [ addon ] , iteratorCallback ) ;
2015-07-20 00:09:47 -07:00
} , callback ) ;
}
function teardownAddons ( app , addons , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
if ( ! addons ) return callback ( null ) ;
2015-07-20 11:03:11 -07:00
debugApp ( app , 'teardownAddons: Tearing down %j' , Object . keys ( addons ) ) ;
2015-07-20 00:09:47 -07:00
async . eachSeries ( Object . keys ( addons ) , function iterator ( addon , iteratorCallback ) {
if ( ! ( addon in KNOWN _ADDONS ) ) return iteratorCallback ( new Error ( 'No such addon:' + addon ) ) ;
2015-10-07 16:10:08 -07:00
debugApp ( app , 'Tearing down addon %s with options %j' , addon , addons [ addon ] ) ;
2015-07-20 00:09:47 -07:00
2015-10-07 16:10:08 -07:00
KNOWN _ADDONS [ addon ] . teardown ( app , addons [ addon ] , iteratorCallback ) ;
2015-07-20 00:09:47 -07:00
} , callback ) ;
}
function backupAddons ( app , addons , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'backupAddons' ) ;
if ( ! addons ) return callback ( null ) ;
2015-07-20 11:03:11 -07:00
debugApp ( app , 'backupAddons: Backing up %j' , Object . keys ( addons ) ) ;
2015-07-20 00:09:47 -07:00
async . eachSeries ( Object . keys ( addons ) , function iterator ( addon , iteratorCallback ) {
if ( ! ( addon in KNOWN _ADDONS ) ) return iteratorCallback ( new Error ( 'No such addon:' + addon ) ) ;
2015-10-07 16:10:08 -07:00
KNOWN _ADDONS [ addon ] . backup ( app , addons [ addon ] , iteratorCallback ) ;
2015-07-20 00:09:47 -07:00
} , callback ) ;
}
2018-09-15 17:05:04 -07:00
function clearAddons ( app , addons , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'clearAddons' ) ;
if ( ! addons ) return callback ( null ) ;
debugApp ( app , 'clearAddons: clearing %j' , Object . keys ( addons ) ) ;
async . eachSeries ( Object . keys ( addons ) , function iterator ( addon , iteratorCallback ) {
if ( ! ( addon in KNOWN _ADDONS ) ) return iteratorCallback ( new Error ( 'No such addon:' + addon ) ) ;
KNOWN _ADDONS [ addon ] . clear ( app , addons [ addon ] , iteratorCallback ) ;
} , callback ) ;
}
2015-07-20 00:09:47 -07:00
function restoreAddons ( app , addons , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'restoreAddons' ) ;
if ( ! addons ) return callback ( null ) ;
2015-07-20 11:03:11 -07:00
debugApp ( app , 'restoreAddons: restoring %j' , Object . keys ( addons ) ) ;
2015-07-20 00:09:47 -07:00
async . eachSeries ( Object . keys ( addons ) , function iterator ( addon , iteratorCallback ) {
if ( ! ( addon in KNOWN _ADDONS ) ) return iteratorCallback ( new Error ( 'No such addon:' + addon ) ) ;
2015-10-07 16:10:08 -07:00
KNOWN _ADDONS [ addon ] . restore ( app , addons [ addon ] , iteratorCallback ) ;
2015-07-20 00:09:47 -07:00
} , callback ) ;
}
2018-10-15 09:18:08 -07:00
function importDatabase ( addon , callback ) {
assert . strictEqual ( typeof addon , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debug ( ` importDatabase: Importing ${ addon } ` ) ;
if ( ! ( addon in KNOWN _ADDONS ) ) return callback ( new Error ( ` No such addon: ${ addon } ` ) ) ;
appdb . getAll ( function ( error , apps ) {
if ( error ) return callback ( error ) ;
async . eachSeries ( apps , function iterator ( app , iteratorCallback ) {
2018-10-15 14:31:35 -07:00
if ( ! ( addon in app . manifest . addons ) ) return iteratorCallback ( ) ; // app doesn't use the addon
debug ( ` importDatabase: Importing addon ${ addon } of app ${ app . id } ` ) ;
async . series ( [
KNOWN _ADDONS [ addon ] . setup . bind ( null , app , app . manifest . addons [ addon ] ) ,
KNOWN _ADDONS [ addon ] . restore . bind ( null , app , app . manifest . addons [ addon ] )
] , iteratorCallback ) ;
2018-10-15 09:18:08 -07:00
} , callback ) ;
} ) ;
}
2018-10-16 14:07:41 -07:00
function updateAddonConfig ( platformConfig , callback ) {
callback = callback || NOOP _CALLBACK ;
// TODO: this should possibly also rollback memory to default
async . eachSeries ( [ 'mysql' , 'postgresql' , 'mail' , 'mongodb' ] , function iterator ( containerName , iteratorCallback ) {
const containerConfig = platformConfig [ containerName ] ;
if ( ! containerConfig ) return iteratorCallback ( ) ;
if ( ! containerConfig . memory || ! containerConfig . memorySwap ) return iteratorCallback ( ) ;
const args = ` update --memory ${ containerConfig . memory } --memory-swap ${ containerConfig . memorySwap } ${ containerName } ` . split ( ' ' ) ;
shell . exec ( ` update ${ containerName } ` , '/usr/bin/docker' , args , { } , iteratorCallback ) ;
} , callback ) ;
}
function startAddons ( existingInfra , callback ) {
assert . strictEqual ( typeof existingInfra , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
let startFuncs = [ ] ;
// always start addons on any infra change, regardless of minor or major update
if ( existingInfra . version !== infra . version ) {
debug ( 'startAddons: no existing infra or infra upgrade. starting all addons' ) ;
startFuncs . push (
startMysql . bind ( null , existingInfra ) ,
startPostgresql . bind ( null , existingInfra ) ,
startMongodb . bind ( null , existingInfra ) ,
mail . startMail ) ;
} else {
assert . strictEqual ( typeof existingInfra . images , 'object' ) ;
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 ) ) ;
if ( infra . images . mail . tag !== existingInfra . images . mail . tag ) startFuncs . push ( mail . startMail ) ;
debug ( 'startAddons: existing infra. incremental addon create %j' , startFuncs . map ( function ( f ) { return f . name ; } ) ) ;
}
async . series ( startFuncs , function ( error ) {
if ( error ) return callback ( error ) ;
settings . getPlatformConfig ( function ( error , platformConfig ) {
if ( error ) return callback ( error ) ;
updateAddonConfig ( platformConfig , callback ) ;
} ) ;
} ) ;
}
2015-07-20 00:09:47 -07:00
function getEnvironment ( app , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-03-25 14:14:57 -07:00
appdb . getAddonConfigByAppId ( app . id , function ( error , result ) {
if ( error ) return callback ( error ) ;
2018-08-13 21:59:02 +02:00
if ( app . manifest . addons [ 'docker' ] ) result . push ( { name : 'DOCKER_HOST' , value : ` tcp://172.18.0.1: ${ config . get ( 'dockerProxyPort' ) } ` } ) ;
2018-08-13 20:47:15 +02:00
2017-03-25 14:14:57 -07:00
return callback ( null , result . map ( function ( e ) { return e . name + '=' + e . value ; } ) ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2018-09-13 13:55:49 -07:00
function getMountsSync ( app , addons ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
2018-09-13 13:55:49 -07:00
let mounts = [ ] ;
2015-07-20 00:09:47 -07:00
2018-09-13 13:55:49 -07:00
if ( ! addons ) return mounts ;
2015-07-20 00:09:47 -07:00
2018-06-21 21:59:47 -07:00
for ( let addon in addons ) {
2015-07-20 00:09:47 -07:00
switch ( addon ) {
2018-06-21 21:59:47 -07:00
case 'localstorage' :
2018-09-13 13:55:49 -07:00
mounts . push ( {
Target : '/app/data' ,
Source : ` ${ app . id } -localstorage ` ,
Type : 'volume' ,
ReadOnly : false
} ) ;
2018-06-21 21:59:47 -07:00
break ;
2015-07-20 00:09:47 -07:00
default : break ;
}
}
2018-09-13 13:55:49 -07:00
return mounts ;
2015-07-20 00:09:47 -07:00
}
2015-11-02 11:20:50 -08:00
function getContainerNamesSync ( app , addons ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert ( ! addons || typeof addons === 'object' ) ;
var names = [ ] ;
if ( ! addons ) return names ;
for ( var addon in addons ) {
switch ( addon ) {
case 'scheduler' :
// names here depend on how scheduler.js creates containers
names = names . concat ( Object . keys ( addons . scheduler ) . map ( function ( taskName ) { return app . id + '-' + taskName ; } ) ) ;
break ;
default : break ;
}
}
return names ;
}
2018-09-13 13:55:49 -07:00
function setupLocalStorage ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'setupLocalStorage' ) ;
// if you change the name, you have to change getMountsSync
docker . createVolume ( app , ` ${ app . id } -localstorage ` , 'data' , callback ) ;
}
2018-09-15 17:05:04 -07:00
function clearLocalStorage ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'clearLocalStorage' ) ;
docker . clearVolume ( app , ` ${ app . id } -localstorage ` , 'data' , callback ) ;
}
2018-09-13 13:55:49 -07:00
function teardownLocalStorage ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'teardownLocalStorage' ) ;
docker . removeVolume ( app , ` ${ app . id } -localstorage ` , 'data' , callback ) ;
}
2015-10-07 16:10:08 -07:00
function setupOauth ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-12-21 00:50:53 -08:00
debugApp ( app , 'setupOauth' ) ;
2016-11-11 10:57:59 +05:30
if ( ! app . sso ) return callback ( null ) ;
2015-07-20 00:09:47 -07:00
var appId = app . id ;
2018-02-08 15:07:49 +01:00
var redirectURI = 'https://' + app . fqdn ;
2018-04-26 15:54:53 -07:00
var scope = accesscontrol . SCOPE _PROFILE ;
2015-07-20 00:09:47 -07:00
2016-06-03 15:05:00 +02:00
clients . delByAppIdAndType ( appId , clients . TYPE _OAUTH , function ( error ) { // remove existing creds
2016-06-13 14:43:56 +02:00
if ( error && error . reason !== ClientsError . NOT _FOUND ) return callback ( error ) ;
2015-07-20 00:09:47 -07:00
2016-06-03 15:11:08 +02:00
clients . add ( appId , clients . TYPE _OAUTH , redirectURI , scope , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( error ) ;
var env = [
2017-03-25 14:14:57 -07:00
{ name : 'OAUTH_CLIENT_ID' , value : result . id } ,
{ name : 'OAUTH_CLIENT_SECRET' , value : result . clientSecret } ,
{ name : 'OAUTH_ORIGIN' , value : config . adminOrigin ( ) }
2015-07-20 00:09:47 -07:00
] ;
debugApp ( app , 'Setting oauth addon config to %j' , env ) ;
appdb . setAddonConfig ( appId , 'oauth' , env , callback ) ;
} ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function teardownOauth ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2015-10-07 15:55:57 -07:00
debugApp ( app , 'teardownOauth' ) ;
2015-07-20 00:09:47 -07:00
2016-06-03 15:05:00 +02:00
clients . delByAppIdAndType ( app . id , clients . TYPE _OAUTH , function ( error ) {
2017-02-07 10:48:51 -08:00
if ( error && error . reason !== ClientsError . NOT _FOUND ) debug ( error ) ;
2015-07-20 00:09:47 -07:00
appdb . unsetAddonConfig ( app . id , 'oauth' , callback ) ;
} ) ;
}
2016-05-12 08:54:59 -07:00
function setupEmail ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-04-03 14:37:52 -07:00
mail . getDomains ( function ( error , mailDomains ) {
2018-03-07 20:39:58 -08:00
if ( error ) return callback ( error ) ;
const mailInDomains = mailDomains . filter ( function ( d ) { return d . enabled ; } ) . map ( function ( d ) { return d . domain ; } ) . join ( ',' ) ;
// note that "external" access info can be derived from MAIL_DOMAIN (since it's part of user documentation)
var env = [
{ name : 'MAIL_SMTP_SERVER' , value : 'mail' } ,
{ name : 'MAIL_SMTP_PORT' , value : '2525' } ,
{ name : 'MAIL_IMAP_SERVER' , value : 'mail' } ,
{ name : 'MAIL_IMAP_PORT' , value : '9993' } ,
{ name : 'MAIL_SIEVE_SERVER' , value : 'mail' } ,
{ name : 'MAIL_SIEVE_PORT' , value : '4190' } ,
{ name : 'MAIL_DOMAIN' , value : app . domain } ,
{ name : 'MAIL_DOMAINS' , value : mailInDomains }
] ;
2016-05-12 08:54:59 -07:00
2018-03-07 20:39:58 -08:00
debugApp ( app , 'Setting up Email' ) ;
2016-05-12 08:54:59 -07:00
2018-03-07 20:39:58 -08:00
appdb . setAddonConfig ( app . id , 'email' , env , callback ) ;
} ) ;
2016-05-12 08:54:59 -07:00
}
function teardownEmail ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Tearing down Email' ) ;
appdb . unsetAddonConfig ( app . id , 'email' , callback ) ;
}
2015-10-07 16:10:08 -07:00
function setupLdap ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2016-11-11 10:57:59 +05:30
if ( ! app . sso ) return callback ( null ) ;
2015-07-20 00:09:47 -07:00
var env = [
2017-03-25 14:14:57 -07:00
{ name : 'LDAP_SERVER' , value : '172.18.0.1' } ,
{ name : 'LDAP_PORT' , value : '' + config . get ( 'ldapPort' ) } ,
{ name : 'LDAP_URL' , value : 'ldap://172.18.0.1:' + config . get ( 'ldapPort' ) } ,
{ name : 'LDAP_USERS_BASE_DN' , value : 'ou=users,dc=cloudron' } ,
{ name : 'LDAP_GROUPS_BASE_DN' , value : 'ou=groups,dc=cloudron' } ,
{ name : 'LDAP_BIND_DN' , value : 'cn=' + app . id + ',ou=apps,dc=cloudron' } ,
{ name : 'LDAP_BIND_PASSWORD' , value : hat ( 4 * 128 ) } // this is ignored
2015-07-20 00:09:47 -07:00
] ;
debugApp ( app , 'Setting up LDAP' ) ;
appdb . setAddonConfig ( app . id , 'ldap' , env , callback ) ;
}
2015-10-07 16:10:08 -07:00
function teardownLdap ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Tearing down LDAP' ) ;
appdb . unsetAddonConfig ( app . id , 'ldap' , callback ) ;
}
2015-10-07 16:10:08 -07:00
function setupSendMail ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2016-09-23 17:20:32 -07:00
debugApp ( app , 'Setting up SendMail' ) ;
2016-01-14 12:56:35 -08:00
2018-05-17 19:48:54 -07:00
appdb . getAddonConfigByName ( app . id , 'sendmail' , 'MAIL_SMTP_PASSWORD' , function ( error , existingPassword ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
2015-07-20 00:09:47 -07:00
2018-06-22 16:37:34 -07:00
var password = error ? hat ( 4 * 48 ) : existingPassword ; // see box#565 for password length
2016-09-27 12:51:25 -07:00
2018-05-17 19:48:54 -07:00
mailboxdb . getByOwnerId ( app . id , function ( error , results ) {
if ( error ) return callback ( error ) ;
var mailbox = results . filter ( function ( r ) { return ! r . aliasTarget ; } ) [ 0 ] ;
var env = [
{ name : 'MAIL_SMTP_SERVER' , value : 'mail' } ,
{ name : 'MAIL_SMTP_PORT' , value : '2525' } ,
{ name : 'MAIL_SMTPS_PORT' , value : '2465' } ,
{ name : 'MAIL_SMTP_USERNAME' , value : mailbox . name + '@' + app . domain } ,
{ name : 'MAIL_SMTP_PASSWORD' , value : password } ,
{ name : 'MAIL_FROM' , value : mailbox . name + '@' + app . domain } ,
{ name : 'MAIL_DOMAIN' , value : app . domain }
] ;
debugApp ( app , 'Setting sendmail addon config to %j' , env ) ;
appdb . setAddonConfig ( app . id , 'sendmail' , env , callback ) ;
} ) ;
2016-05-15 21:23:44 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
}
2015-10-07 16:10:08 -07:00
function teardownSendMail ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2016-09-23 17:20:32 -07:00
debugApp ( app , 'Tearing down sendmail' ) ;
2016-05-15 21:23:44 -07:00
2016-09-23 17:20:32 -07:00
appdb . unsetAddonConfig ( app . id , 'sendmail' , callback ) ;
2015-07-20 00:09:47 -07:00
}
2016-05-13 14:13:25 -07:00
function setupRecvMail ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Setting up recvmail' ) ;
2018-05-17 19:48:54 -07:00
appdb . getAddonConfigByName ( app . id , 'recvmail' , 'MAIL_IMAP_PASSWORD' , function ( error , existingPassword ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
2016-05-13 14:13:25 -07:00
2018-06-22 16:37:34 -07:00
var password = error ? hat ( 4 * 48 ) : existingPassword ; // see box#565 for password length
2016-09-27 12:51:25 -07:00
2018-05-17 19:48:54 -07:00
mailboxdb . getByOwnerId ( app . id , function ( error , results ) {
if ( error ) return callback ( error ) ;
2016-09-23 17:20:32 -07:00
2018-05-17 19:48:54 -07:00
var mailbox = results . filter ( function ( r ) { return ! r . aliasTarget ; } ) [ 0 ] ;
var env = [
{ name : 'MAIL_IMAP_SERVER' , value : 'mail' } ,
{ name : 'MAIL_IMAP_PORT' , value : '9993' } ,
{ name : 'MAIL_IMAP_USERNAME' , value : mailbox . name + '@' + app . domain } ,
{ name : 'MAIL_IMAP_PASSWORD' , value : password } ,
{ name : 'MAIL_TO' , value : mailbox . name + '@' + app . domain } ,
{ name : 'MAIL_DOMAIN' , value : app . domain }
] ;
debugApp ( app , 'Setting sendmail addon config to %j' , env ) ;
appdb . setAddonConfig ( app . id , 'recvmail' , env , callback ) ;
} ) ;
2016-05-13 14:13:25 -07:00
} ) ;
}
function teardownRecvMail ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2016-09-23 17:20:32 -07:00
debugApp ( app , 'Tearing down recvmail' ) ;
2016-05-13 14:13:25 -07:00
2016-09-23 17:20:32 -07:00
appdb . unsetAddonConfig ( app . id , 'recvmail' , callback ) ;
2016-05-13 14:13:25 -07:00
}
2018-06-04 12:23:46 -07:00
function mysqlDatabaseName ( appId ) {
2018-05-16 19:49:29 -07:00
assert . strictEqual ( typeof appId , 'string' ) ;
var md5sum = crypto . createHash ( 'md5' ) ; // get rid of "-"
md5sum . update ( appId ) ;
2018-06-04 12:23:46 -07:00
return md5sum . digest ( 'hex' ) . substring ( 0 , 16 ) ; // max length of mysql usernames is 16
2018-05-16 19:49:29 -07:00
}
2018-10-16 14:07:41 -07:00
function startMysql ( existingInfra , callback ) {
assert . strictEqual ( typeof existingInfra , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
const tag = infra . images . mysql . 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 ;
const upgrading = existingInfra . version !== 'none' && requiresUpgrade ( existingInfra . images . mysql . tag , tag ) ;
if ( upgrading ) {
debug ( 'startMysql: mysql will be upgraded' ) ;
shell . sudoSync ( 'startMysql' , ` ${ RMADDON _CMD } mysql ` ) ;
}
const cmd = ` docker run --restart=always -d --name="mysql" \
-- net cloudron \
-- net - alias mysql \
-- log - driver syslog \
-- log - opt syslog - address = udp : //127.0.0.1:2514 \
-- log - opt syslog - format = rfc5424 \
-- log - opt tag = mysql \
- m $ { memoryLimit } m \
-- memory - swap $ { memoryLimit * 2 } m \
-- dns 172.18 . 0.1 \
-- dns - search = . \
- e CLOUDRON _MYSQL _TOKEN = $ { cloudronToken } \
- e CLOUDRON _MYSQL _ROOT _HOST = 172.18 . 0.1 \
- e CLOUDRON _MYSQL _ROOT _PASSWORD = $ { rootPassword } \
- v "${dataDir}/mysql:/var/lib/mysql" \
-- read - only - v / tmp - v / run "${tag}" ` ;
shell . execSync ( 'startMysql' , cmd ) ;
waitForAddon ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error ) {
if ( error ) return callback ( error ) ;
if ( ! upgrading ) return callback ( null ) ;
importDatabase ( 'mysql' , callback ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function setupMySql ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Setting up mysql' ) ;
2018-05-17 19:48:54 -07:00
appdb . getAddonConfigByName ( app . id , 'mysql' , 'MYSQL_PASSWORD' , function ( error , existingPassword ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
2018-05-16 19:49:29 -07:00
2018-09-18 19:41:24 +02:00
const tmp = mysqlDatabaseName ( app . id ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
const data = {
database : tmp ,
prefix : tmp ,
username : tmp ,
password : error ? hat ( 4 * 48 ) : existingPassword // see box#362 for password length
} ;
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
getAddonDetails ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error , result ) {
2018-05-17 19:48:54 -07:00
if ( error ) return callback ( error ) ;
2018-05-16 19:49:29 -07:00
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/ ` + ( options . multipleDatabases ? 'prefixes' : 'databases' ) + ` ?access_token= ${ result . token } ` , { rejectUnauthorized : false , json : data } , function ( error , response ) {
2018-09-18 19:41:24 +02:00
if ( error ) return callback ( new Error ( 'Error setting up mysql: ' + error ) ) ;
2018-09-24 16:45:02 -07:00
if ( response . statusCode !== 201 ) return callback ( new Error ( ` Error setting up mysql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 19:41:24 +02:00
var env = [
{ name : 'MYSQL_USERNAME' , value : data . username } ,
{ name : 'MYSQL_PASSWORD' , value : data . password } ,
{ name : 'MYSQL_HOST' , value : 'mysql' } ,
{ name : 'MYSQL_PORT' , value : '3306' }
] ;
if ( options . multipleDatabases ) {
env = env . concat ( { name : 'MYSQL_DATABASE_PREFIX' , value : ` ${ data . prefix } _ ` } ) ;
} else {
env = env . concat (
{ name : 'MYSQL_URL' , value : ` mysql:// ${ data . username } : ${ data . password } @mysql/ ${ data . database } ` } ,
{ name : 'MYSQL_DATABASE' , value : data . database }
) ;
}
debugApp ( app , 'Setting mysql addon config to %j' , env ) ;
appdb . setAddonConfig ( app . id , 'mysql' , env , callback ) ;
} ) ;
2018-05-17 19:48:54 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-09-15 17:05:04 -07:00
function clearMySql ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-18 19:41:24 +02:00
const database = mysqlDatabaseName ( app . id ) ;
2018-09-15 17:05:04 -07:00
2018-09-18 19:41:24 +02:00
getAddonDetails ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error , result ) {
2018-09-15 17:05:04 -07:00
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/ ` + ( options . multipleDatabases ? 'prefixes' : 'databases' ) + ` / ${ database } /clear?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 19:41:24 +02:00
if ( error ) return callback ( new Error ( 'Error clearing mysql: ' + error ) ) ;
2018-09-24 16:45:02 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error clearing mysql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 19:41:24 +02:00
callback ( ) ;
} ) ;
2018-09-15 17:05:04 -07:00
} ) ;
}
2015-10-07 16:10:08 -07:00
function teardownMySql ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-18 19:41:24 +02:00
const database = mysqlDatabaseName ( app . id ) ;
const username = database ;
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
getAddonDetails ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . delete ( ` https:// ${ result . ip } :3000/ ` + ( options . multipleDatabases ? 'prefixes' : 'databases' ) + ` / ${ database } ?access_token= ${ result . token } &username= ${ username } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 19:41:24 +02:00
if ( error ) return callback ( new Error ( 'Error clearing mysql: ' + error ) ) ;
2018-09-24 16:45:02 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error clearing mysql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 19:41:24 +02:00
appdb . unsetAddonConfig ( app . id , 'mysql' , callback ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2015-10-07 16:10:08 -07:00
function backupMySql ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-18 19:41:24 +02:00
const database = mysqlDatabaseName ( app . id ) ;
2015-07-20 00:09:47 -07:00
debugApp ( app , 'Backing up mysql' ) ;
2018-09-18 19:41:24 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
getAddonDetails ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
const writeStream = fs . createWriteStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'mysqldump' ) ) ;
writeStream . on ( 'error' , callback ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 20:05:55 +02:00
const req = request . post ( ` https:// ${ result . ip } :3000/ ` + ( options . multipleDatabases ? 'prefixes' : 'databases' ) + ` / ${ database } /backup?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 19:41:24 +02:00
if ( error ) return callback ( error ) ;
2018-09-24 16:45:02 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from mysql addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 19:41:24 +02:00
callback ( null ) ;
} ) ;
req . pipe ( writeStream ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2015-10-07 16:10:08 -07:00
function restoreMySql ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-18 19:41:24 +02:00
const database = mysqlDatabaseName ( app . id ) ;
2015-07-20 00:09:47 -07:00
2018-09-15 17:05:04 -07:00
debugApp ( app , 'restoreMySql' ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 19:41:24 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
getAddonDetails ( 'mysql' , 'CLOUDRON_MYSQL_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
var input = fs . createReadStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'mysqldump' ) ) ;
input . on ( 'error' , callback ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 20:05:55 +02:00
const restoreReq = request . post ( ` https:// ${ result . ip } :3000/ ` + ( options . multipleDatabases ? 'prefixes' : 'databases' ) + ` / ${ database } /restore?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 19:41:24 +02:00
if ( error ) return callback ( error ) ;
2018-09-24 16:45:02 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from mysql addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 19:41:24 +02:00
callback ( null ) ;
} ) ;
input . pipe ( restoreReq ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2018-09-19 15:13:04 -07:00
function postgreSqlNames ( appId ) {
appId = appId . replace ( /-/g , '' ) ;
2018-09-19 15:46:29 -07:00
return { database : ` db ${ appId } ` , username : ` user ${ appId } ` } ;
2018-09-19 15:13:04 -07:00
}
2018-10-16 14:07:41 -07:00
function startPostgresql ( existingInfra , callback ) {
assert . strictEqual ( typeof existingInfra , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
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 ;
const upgrading = existingInfra . version !== 'none' && requiresUpgrade ( existingInfra . images . postgresql . tag , tag ) ;
if ( upgrading ) {
debug ( 'startPostgresql: postgresql will be upgraded' ) ;
shell . sudoSync ( 'startPostgresql' , ` ${ RMADDON _CMD } postgresql ` ) ;
}
const cmd = ` docker run --restart=always -d --name="postgresql" \
-- net cloudron \
-- net - alias postgresql \
-- log - driver syslog \
-- log - opt syslog - address = udp : //127.0.0.1:2514 \
-- log - opt syslog - format = rfc5424 \
-- log - opt tag = postgresql \
- m $ { memoryLimit } m \
-- memory - swap $ { memoryLimit * 2 } m \
-- dns 172.18 . 0.1 \
-- dns - search = . \
- e CLOUDRON _POSTGRESQL _ROOT _PASSWORD = "${rootPassword}" \
- e CLOUDRON _POSTGRESQL _TOKEN = "${cloudronToken}" \
- v "${dataDir}/postgresql:/var/lib/postgresql" \
-- read - only - v / tmp - v / run "${tag}" ` ;
shell . execSync ( 'startPostgresql' , cmd ) ;
waitForAddon ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error ) {
if ( error ) return callback ( error ) ;
if ( ! upgrading ) return callback ( null ) ;
importDatabase ( 'postgresql' , callback ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function setupPostgreSql ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Setting up postgresql' ) ;
2018-09-19 15:13:04 -07:00
const { database , username } = postgreSqlNames ( app . id ) ;
2015-07-20 00:09:47 -07:00
2018-09-19 15:13:04 -07:00
appdb . getAddonConfigByName ( app . id , 'postgresql' , 'POSTGRESQL_PASSWORD' , function ( error , existingPassword ) {
2018-09-16 22:21:34 +02:00
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
2018-05-16 20:04:35 -07:00
2018-09-16 22:21:34 +02:00
const data = {
2018-09-19 15:13:04 -07:00
database : database ,
username : username ,
2018-09-16 22:21:34 +02:00
password : error ? hat ( 4 * 128 ) : existingPassword
} ;
2015-07-20 00:09:47 -07:00
2018-09-16 22:21:34 +02:00
getAddonDetails ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error , result ) {
2018-05-17 19:48:54 -07:00
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/databases?access_token= ${ result . token } ` , { rejectUnauthorized : false , json : data } , function ( error , response ) {
2018-09-16 22:21:34 +02:00
if ( error ) return callback ( new Error ( 'Error setting up postgresql: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 201 ) return callback ( new Error ( ` Error setting up postgresql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-16 22:21:34 +02:00
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 ) ;
} ) ;
2018-05-17 19:48:54 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-09-15 17:05:04 -07:00
function clearPostgreSql ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-19 15:13:04 -07:00
const { database , username } = postgreSqlNames ( app . id ) ;
2018-09-15 17:05:04 -07:00
debugApp ( app , 'Clearing postgresql' ) ;
2018-09-16 22:21:34 +02:00
getAddonDetails ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error , result ) {
2018-09-15 17:05:04 -07:00
if ( error ) return callback ( error ) ;
2018-09-19 15:13:04 -07:00
request . post ( ` https:// ${ result . ip } :3000/databases/ ${ database } /clear?access_token= ${ result . token } &username= ${ username } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-16 22:21:34 +02:00
if ( error ) return callback ( new Error ( 'Error clearing postgresql: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error clearing postgresql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-16 22:21:34 +02:00
callback ( null ) ;
} ) ;
2018-09-15 17:05:04 -07:00
} ) ;
}
2015-10-07 16:10:08 -07:00
function teardownPostgreSql ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-19 15:13:04 -07:00
const { database , username } = postgreSqlNames ( app . id ) ;
2018-05-16 20:04:35 -07:00
2018-09-16 22:21:34 +02:00
getAddonDetails ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( error ) ;
2018-09-19 15:13:04 -07:00
request . delete ( ` https:// ${ result . ip } :3000/databases/ ${ database } ?access_token= ${ result . token } &username= ${ username } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-16 22:21:34 +02:00
if ( error ) return callback ( new Error ( 'Error tearing down postgresql: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error tearing down postgresql. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-16 22:21:34 +02:00
appdb . unsetAddonConfig ( app . id , 'postgresql' , callback ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2015-10-07 16:10:08 -07:00
function backupPostgreSql ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2015-07-20 00:09:47 -07:00
debugApp ( app , 'Backing up postgresql' ) ;
2018-09-19 15:13:04 -07:00
const { database } = postgreSqlNames ( app . id ) ;
2018-09-16 22:21:34 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
2015-07-20 00:09:47 -07:00
2018-09-16 22:21:34 +02:00
getAddonDetails ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
2015-07-20 00:09:47 -07:00
2018-09-16 22:21:34 +02:00
const writeStream = fs . createWriteStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'postgresqldump' ) ) ;
writeStream . on ( 'error' , callback ) ;
2018-09-19 15:13:04 -07:00
const req = request . post ( ` https:// ${ result . ip } :3000/databases/ ${ database } /backup?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-16 22:21:34 +02:00
if ( error ) return callback ( error ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from postgresql addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2015-07-20 00:09:47 -07:00
2018-09-16 22:21:34 +02:00
callback ( null ) ;
} ) ;
req . pipe ( writeStream ) ;
} ) ;
2015-07-20 00:09:47 -07:00
}
2015-10-07 16:10:08 -07:00
function restorePostgreSql ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-16 22:21:34 +02:00
debugApp ( app , 'Restore postgresql' ) ;
2015-07-20 00:09:47 -07:00
2018-09-19 15:13:04 -07:00
const { database , username } = postgreSqlNames ( app . id ) ;
2015-07-20 00:09:47 -07:00
2018-09-16 22:21:34 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
2015-07-20 00:09:47 -07:00
2018-09-18 12:28:28 -07:00
getAddonDetails ( 'postgresql' , 'CLOUDRON_POSTGRESQL_TOKEN' , function ( error , result ) {
2018-09-16 22:21:34 +02:00
if ( error ) return callback ( error ) ;
2015-07-20 00:09:47 -07:00
2018-09-20 12:15:50 -07:00
var input = fs . createReadStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'postgresqldump' ) ) ;
input . on ( 'error' , callback ) ;
2018-09-16 22:21:34 +02:00
2018-09-19 15:13:04 -07:00
const restoreReq = request . post ( ` https:// ${ result . ip } :3000/databases/ ${ database } /restore?access_token= ${ result . token } &username= ${ username } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 12:28:28 -07:00
if ( error ) return callback ( error ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from postgresql addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-16 22:21:34 +02:00
2018-09-18 12:28:28 -07:00
callback ( null ) ;
2018-09-16 22:21:34 +02:00
} ) ;
2018-09-18 12:28:28 -07:00
input . pipe ( restoreReq ) ;
2018-09-16 22:21:34 +02:00
} ) ;
2015-07-20 00:09:47 -07:00
}
2018-10-16 14:07:41 -07:00
function startMongodb ( existingInfra , callback ) {
assert . strictEqual ( typeof existingInfra , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
const tag = infra . images . mongodb . 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 ) ) * 200 ;
const upgrading = existingInfra . version !== 'none' && requiresUpgrade ( existingInfra . images . mongodb . tag , tag ) ;
if ( upgrading ) {
debug ( 'startMongodb: mongodb will be upgraded' ) ;
shell . sudoSync ( 'startMongodb' , ` ${ RMADDON _CMD } mongodb ` ) ;
}
const cmd = ` docker run --restart=always -d --name="mongodb" \
-- net cloudron \
-- net - alias mongodb \
-- log - driver syslog \
-- log - opt syslog - address = udp : //127.0.0.1:2514 \
-- log - opt syslog - format = rfc5424 \
-- log - opt tag = mongodb \
- m $ { memoryLimit } m \
-- memory - swap $ { memoryLimit * 2 } m \
-- dns 172.18 . 0.1 \
-- dns - search = . \
- e CLOUDRON _MONGODB _ROOT _PASSWORD = "${rootPassword}" \
- e CLOUDRON _MONGODB _TOKEN = "${cloudronToken}" \
- v "${dataDir}/mongodb:/var/lib/mongodb" \
-- read - only - v / tmp - v / run "${tag}" ` ;
shell . execSync ( 'startMongodb' , cmd ) ;
waitForAddon ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error ) {
if ( error ) return callback ( error ) ;
if ( ! upgrading ) return callback ( null ) ;
importDatabase ( 'mongodb' , callback ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function setupMongoDb ( app , options , callback ) {
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof app , 'object' ) ;
2015-10-07 16:10:08 -07:00
assert . strictEqual ( typeof options , 'object' ) ;
2015-07-20 00:09:47 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Setting up mongodb' ) ;
2018-05-17 19:48:54 -07:00
appdb . getAddonConfigByName ( app . id , 'mongodb' , 'MONGODB_PASSWORD' , function ( error , existingPassword ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
2018-05-16 20:59:11 -07:00
2018-09-11 19:27:06 +02:00
const data = {
database : app . id ,
username : app . id ,
password : error ? hat ( 4 * 128 ) : existingPassword
} ;
2018-05-17 19:48:54 -07:00
2018-09-15 20:38:18 -07:00
getAddonDetails ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error , result ) {
2018-05-17 19:48:54 -07:00
if ( error ) return callback ( error ) ;
2017-03-25 14:14:57 -07:00
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/databases?access_token= ${ result . token } ` , { rejectUnauthorized : false , json : data } , function ( error , response ) {
2018-09-11 19:27:06 +02:00
if ( error ) return callback ( new Error ( 'Error setting up mongodb: ' + error ) ) ;
2018-09-13 12:37:21 +02:00
if ( response . statusCode !== 201 ) return callback ( new Error ( ` Error setting up mongodb. Status code: ${ response . statusCode } ` ) ) ;
2018-09-11 19:27:06 +02:00
var env = [
2018-09-19 17:25:40 -07:00
{ name : 'MONGODB_URL' , value : ` mongodb:// ${ data . username } : ${ data . password } @mongodb/ ${ data . database } ` } ,
2018-09-11 19:27:06 +02:00
{ name : 'MONGODB_USERNAME' , value : data . username } ,
{ name : 'MONGODB_PASSWORD' , value : data . password } ,
{ name : 'MONGODB_HOST' , value : 'mongodb' } ,
{ name : 'MONGODB_PORT' , value : '27017' } ,
{ name : 'MONGODB_DATABASE' , value : data . database }
] ;
debugApp ( app , 'Setting mongodb addon config to %j' , env ) ;
appdb . setAddonConfig ( app . id , 'mongodb' , env , callback ) ;
} ) ;
2018-05-17 19:48:54 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-09-15 17:05:04 -07:00
function clearMongodb ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Clearing mongodb' ) ;
2018-09-15 20:38:18 -07:00
getAddonDetails ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error , result ) {
2018-09-15 17:05:04 -07:00
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/databases/ ${ app . id } /clear?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-15 17:05:04 -07:00
if ( error ) return callback ( new Error ( 'Error clearing mongodb: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error clearing mongodb. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-15 17:05:04 -07:00
callback ( ) ;
} ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function teardownMongoDb ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2015-07-20 00:09:47 -07:00
debugApp ( app , 'Tearing down mongodb' ) ;
2018-09-15 20:38:18 -07:00
getAddonDetails ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . delete ( ` https:// ${ result . ip } :3000/databases/ ${ app . id } ?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-11 19:27:06 +02:00
if ( error ) return callback ( new Error ( 'Error tearing down mongodb: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error tearing down mongodb. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-11 19:27:06 +02:00
appdb . unsetAddonConfig ( app . id , 'mongodb' , callback ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2015-10-07 16:10:08 -07:00
function backupMongoDb ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2015-07-20 00:09:47 -07:00
debugApp ( app , 'Backing up mongodb' ) ;
2018-09-11 21:43:16 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
2015-07-20 00:09:47 -07:00
2018-09-15 20:38:18 -07:00
getAddonDetails ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error , result ) {
2018-09-11 21:43:16 +02:00
if ( error ) return callback ( error ) ;
2018-09-13 12:37:21 +02:00
const writeStream = fs . createWriteStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'mongodbdump' ) ) ;
writeStream . on ( 'error' , callback ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 20:05:55 +02:00
const req = request . post ( ` https:// ${ result . ip } :3000/databases/ ${ app . id } /backup?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-13 12:37:21 +02:00
if ( error ) return callback ( error ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from mongodb addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2015-07-20 00:09:47 -07:00
2018-09-13 12:37:21 +02:00
callback ( null ) ;
} ) ;
req . pipe ( writeStream ) ;
2018-09-11 21:43:16 +02:00
} ) ;
2015-07-20 00:09:47 -07:00
}
2015-10-07 16:10:08 -07:00
function restoreMongoDb ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-11 21:43:16 +02:00
callback = once ( callback ) ; // protect from multiple returns with streams
2015-07-20 00:09:47 -07:00
2018-09-15 17:05:04 -07:00
debugApp ( app , 'restoreMongoDb' ) ;
2018-09-15 20:38:18 -07:00
getAddonDetails ( 'mongodb' , 'CLOUDRON_MONGODB_TOKEN' , function ( error , result ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( error ) ;
2018-09-15 17:05:04 -07:00
const readStream = fs . createReadStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'mongodbdump' ) ) ;
readStream . on ( 'error' , callback ) ;
2015-07-20 00:09:47 -07:00
2018-09-18 20:05:55 +02:00
const restoreReq = request . post ( ` https:// ${ result . ip } :3000/databases/ ${ app . id } /restore?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-11 21:43:16 +02:00
if ( error ) return callback ( error ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from mongodb addon ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-11 21:43:16 +02:00
2018-09-15 17:05:04 -07:00
callback ( null ) ;
2018-09-11 21:43:16 +02:00
} ) ;
2018-09-15 17:05:04 -07:00
readStream . pipe ( restoreReq ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
// Ensures that app's addon redis container is running. Can be called when named container already exists/running
2015-10-07 16:10:08 -07:00
function setupRedis ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-09-18 14:15:23 -07:00
const redisName = 'redis-' + app . id ;
2015-07-20 00:09:47 -07:00
2018-09-18 14:15:23 -07:00
docker . inspect ( redisName , function ( error , result ) {
if ( ! error ) {
debug ( ` Re-using existing redis container with state: ${ result . State } ` ) ;
return callback ( ) ;
2018-05-17 19:48:54 -07:00
}
2018-09-18 14:15:23 -07:00
appdb . getAddonConfigByName ( app . id , 'redis' , 'REDIS_PASSWORD' , function ( error , existingPassword ) {
if ( error && error . reason !== DatabaseError . NOT _FOUND ) return callback ( error ) ;
const redisPassword = error ? hat ( 4 * 48 ) : existingPassword ; // see box#362 for password length
const redisServiceToken = hat ( 4 * 48 ) ;
// Compute redis memory limit based on app's memory limit (this is arbitrary)
var memoryLimit = app . memoryLimit || app . manifest . memoryLimit || 0 ;
if ( memoryLimit === - 1 ) { // unrestricted (debug mode)
memoryLimit = 0 ;
} else if ( memoryLimit === 0 || memoryLimit <= ( 2 * 1024 * 1024 * 1024 ) ) { // less than 2G (ram+swap)
memoryLimit = 150 * 1024 * 1024 ; // 150m
} else {
memoryLimit = 600 * 1024 * 1024 ; // 600m
}
const tag = infra . images . redis . tag ;
const label = app . fqdn ;
// note that we do not add appId label because this interferes with the stop/start app logic
const cmd = ` docker run --restart=always -d --name= ${ redisName } \
-- label = location = $ { label } \
-- net cloudron \
-- net - alias $ { redisName } \
-- log - driver syslog \
-- log - opt syslog - address = udp : //127.0.0.1:2514 \
-- log - opt syslog - format = rfc5424 \
-- log - opt tag = "${redisName}" \
- m $ { memoryLimit / 2 } \
-- memory - swap $ { memoryLimit } \
-- dns 172.18 . 0.1 \
-- dns - search = . \
- e CLOUDRON _REDIS _PASSWORD = "${redisPassword}" \
- e CLOUDRON _REDIS _TOKEN = "${redisServiceToken}" \
- v "${paths.PLATFORM_DATA_DIR}/redis/${app.id}:/var/lib/redis" \
-- read - only - v / tmp - v / run $ { tag } ` ;
2015-07-20 00:09:47 -07:00
2018-09-18 14:15:23 -07:00
var env = [
{ name : 'REDIS_URL' , value : 'redis://redisuser:' + redisPassword + '@redis-' + app . id } ,
{ name : 'REDIS_PASSWORD' , value : redisPassword } ,
{ name : 'REDIS_HOST' , value : redisName } ,
{ name : 'REDIS_PORT' , value : '6379' }
] ;
2018-05-17 19:48:54 -07:00
2018-09-18 14:15:23 -07:00
async . series ( [
shell . execSync . bind ( null , 'startRedis' , cmd ) ,
appdb . setAddonConfig . bind ( null , app . id , 'redis' , env ) ,
2018-10-14 13:26:01 -07:00
waitForAddon . bind ( null , 'redis-' + app . id , 'CLOUDRON_REDIS_TOKEN' )
2018-09-18 14:15:23 -07:00
] , function ( error ) {
if ( error ) debug ( 'Error setting up redis: ' , error ) ;
callback ( error ) ;
} ) ;
2018-05-17 19:48:54 -07:00
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
}
2018-09-15 17:05:04 -07:00
function clearRedis ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Clearing redis' ) ;
getAddonDetails ( 'redis-' + app . id , 'CLOUDRON_REDIS_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
2018-09-18 20:05:55 +02:00
request . post ( ` https:// ${ result . ip } :3000/clear?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-15 17:05:04 -07:00
if ( error ) return callback ( new Error ( 'Error clearing redis: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error clearing redis. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-15 17:05:04 -07:00
callback ( null ) ;
} ) ;
} ) ;
}
2015-10-07 16:10:08 -07:00
function teardownRedis ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-01-17 21:05:43 -08:00
var container = dockerConnection . getContainer ( 'redis-' + app . id ) ;
2015-07-20 00:09:47 -07:00
2018-01-17 21:05:43 -08:00
var removeOptions = {
force : true , // kill container if it's running
v : true // removes volumes associated with the container
} ;
2015-07-20 00:09:47 -07:00
2018-01-17 21:05:43 -08:00
container . remove ( removeOptions , function ( error ) {
if ( error && error . statusCode !== 404 ) return callback ( new Error ( 'Error removing container:' + error ) ) ;
2015-07-20 00:09:47 -07:00
2018-10-15 14:38:33 -07:00
shell . sudo ( 'removeVolume' , [ RMADDON _CMD , 'redis' , app . id ] , function ( error ) {
2015-07-20 00:09:47 -07:00
if ( error ) return callback ( new Error ( 'Error removing redis data:' + error ) ) ;
2018-09-18 14:15:23 -07:00
rimraf ( path . join ( paths . LOG _DIR , ` redis- ${ app . id } ` ) , function ( error ) {
if ( error ) debugApp ( app , 'cannot cleanup logs: %s' , error ) ;
appdb . unsetAddonConfig ( app . id , 'redis' , callback ) ;
} ) ;
2015-07-20 00:09:47 -07:00
} ) ;
2018-01-17 21:05:43 -08:00
} ) ;
2015-07-20 00:09:47 -07:00
}
2015-10-12 13:29:27 -07:00
function backupRedis ( app , options , callback ) {
2018-02-08 15:07:49 +01:00
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2015-10-12 13:29:27 -07:00
debugApp ( app , 'Backing up redis' ) ;
2018-09-18 12:28:03 -07:00
callback = once ( callback ) ; // protect from multiple returns with streams
2018-09-14 13:26:56 +02:00
getAddonDetails ( 'redis-' + app . id , 'CLOUDRON_REDIS_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
2018-09-07 21:50:32 +02:00
2018-09-18 12:28:03 -07:00
const writeStream = fs . createWriteStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'dump.rdb' ) ) ;
writeStream . on ( 'error' , callback ) ;
const req = request . post ( ` https:// ${ result . ip } :3000/backup?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-14 13:19:33 +02:00
if ( error ) return callback ( new Error ( 'Error backing up redis: ' + error ) ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Error backing up redis. Status code: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-07 21:50:32 +02:00
2018-09-14 13:19:33 +02:00
callback ( null ) ;
2018-09-07 21:50:32 +02:00
} ) ;
2018-09-18 12:28:03 -07:00
req . pipe ( writeStream ) ;
} ) ;
}
function restoreRedis ( app , options , callback ) {
assert . strictEqual ( typeof app , 'object' ) ;
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
debugApp ( app , 'Restoring redis' ) ;
getAddonDetails ( 'redis-' + app . id , 'CLOUDRON_REDIS_TOKEN' , function ( error , result ) {
if ( error ) return callback ( error ) ;
2018-09-26 09:48:18 -07:00
let input ;
let oldDumpLocation = path . join ( paths . APPS _DATA _DIR , app . id , 'redis/dump.rdb' ) ;
if ( fs . existsSync ( oldDumpLocation ) ) {
input = fs . createReadStream ( oldDumpLocation ) ;
} else {
input = fs . createReadStream ( path . join ( paths . APPS _DATA _DIR , app . id , 'dump.rdb' ) ) ;
}
2018-09-18 12:28:03 -07:00
input . on ( 'error' , callback ) ;
2018-09-18 14:15:23 -07:00
const restoreReq = request . post ( ` https:// ${ result . ip } :3000/restore?access_token= ${ result . token } ` , { rejectUnauthorized : false } , function ( error , response ) {
2018-09-18 12:28:03 -07:00
if ( error ) return callback ( error ) ;
2018-09-19 15:46:29 -07:00
if ( response . statusCode !== 200 ) return callback ( new Error ( ` Unexpected response from redis addon: ${ response . statusCode } message: ${ response . body . message } ` ) ) ;
2018-09-18 12:28:03 -07:00
callback ( null ) ;
} ) ;
input . pipe ( restoreReq ) ;
2018-09-07 21:50:32 +02:00
} ) ;
2015-10-12 13:29:27 -07:00
}