2017-12-09 08:08:15 +05:30
'use strict' ;
exports = module . exports = {
2018-01-18 13:16:40 -08:00
verifySetupToken : verifySetupToken ,
setupDone : setupDone ,
2017-12-09 08:42:15 +05:30
changePlan : changePlan ,
2018-01-02 12:47:33 -08:00
upgrade : upgrade ,
2018-01-02 13:05:30 -08:00
sendHeartbeat : sendHeartbeat ,
2018-01-01 19:19:07 -08:00
getBoxAndUserDetails : getBoxAndUserDetails ,
2018-01-18 13:16:40 -08:00
setPtrRecord : setPtrRecord ,
CaasError : CaasError
2017-12-09 08:08:15 +05:30
} ;
var assert = require ( 'assert' ) ,
backups = require ( './backups.js' ) ,
config = require ( './config.js' ) ,
debug = require ( 'debug' ) ( 'box:caas' ) ,
locker = require ( './locker.js' ) ,
path = require ( 'path' ) ,
progress = require ( './progress.js' ) ,
2018-01-17 12:26:53 -08:00
settings = require ( './settings.js' ) ,
2017-12-09 08:08:15 +05:30
shell = require ( './shell.js' ) ,
superagent = require ( 'superagent' ) ,
util = require ( 'util' ) ,
_ = require ( 'underscore' ) ;
const RETIRE _CMD = path . join ( _ _dirname , 'scripts/retire.sh' ) ;
function CaasError ( reason , errorOrMessage ) {
assert . strictEqual ( typeof reason , 'string' ) ;
assert ( errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined' ) ;
Error . call ( this ) ;
Error . captureStackTrace ( this , this . constructor ) ;
this . name = this . constructor . name ;
this . reason = reason ;
if ( typeof errorOrMessage === 'undefined' ) {
this . message = reason ;
} else if ( typeof errorOrMessage === 'string' ) {
this . message = errorOrMessage ;
} else {
this . message = 'Internal error' ;
this . nestedError = errorOrMessage ;
}
}
util . inherits ( CaasError , Error ) ;
CaasError . BAD _FIELD = 'Field error' ;
2018-01-18 13:16:40 -08:00
CaasError . BAD _STATE = 'Bad state' ;
CaasError . INVALID _TOKEN = 'Invalid Token' ;
2017-12-09 08:08:15 +05:30
CaasError . INTERNAL _ERROR = 'Internal Error' ;
CaasError . EXTERNAL _ERROR = 'External Error' ;
var NOOP _CALLBACK = function ( error ) { if ( error ) debug ( error ) ; } ;
function retire ( reason , info , callback ) {
assert ( reason === 'migrate' || reason === 'upgrade' ) ;
info = info || { } ;
callback = callback || NOOP _CALLBACK ;
var data = {
apiServerOrigin : config . apiServerOrigin ( ) ,
2018-01-18 19:44:43 -08:00
adminFqdn : config . adminFqdn ( )
2017-12-09 08:08:15 +05:30
} ;
shell . sudo ( 'retire' , [ RETIRE _CMD , reason , JSON . stringify ( info ) , JSON . stringify ( data ) ] , callback ) ;
}
2018-01-17 12:26:53 -08:00
function getCaasConfig ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
settings . getCaasConfig ( function ( error , result ) {
if ( error ) return callback ( new CaasError ( CaasError . INTERNAL _ERROR , error ) ) ;
callback ( null , result ) ;
} ) ;
}
2018-01-18 13:16:40 -08:00
function verifySetupToken ( setupToken , callback ) {
assert . strictEqual ( typeof setupToken , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
settings . getCaasConfig ( function ( error , caasConfig ) {
if ( error ) return callback ( new CaasError ( CaasError . INTERNAL _ERROR , error ) ) ;
superagent . get ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + caasConfig . boxId + '/setup/verify' ) . query ( { setupToken : setupToken } )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , error ) ) ;
if ( result . statusCode === 403 ) return callback ( new CaasError ( CaasError . INVALID _TOKEN ) ) ;
if ( result . statusCode === 409 ) return callback ( new CaasError ( CaasError . BAD _STATE , 'Already setup' ) ) ;
if ( result . statusCode !== 200 ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
} ) ;
}
function setupDone ( setupToken , callback ) {
assert . strictEqual ( typeof setupToken , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
settings . getCaasConfig ( function ( error , caasConfig ) {
if ( error ) return callback ( new CaasError ( CaasError . INTERNAL _ERROR , error ) ) ;
// Now let the api server know we got activated
superagent . post ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + caasConfig . boxId + '/setup/done' ) . query ( { setupToken : setupToken } )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , error ) ) ;
if ( result . statusCode === 403 ) return callback ( new CaasError ( CaasError . INVALID _TOKEN ) ) ;
if ( result . statusCode === 409 ) return callback ( new CaasError ( CaasError . BAD _STATE , 'Already setup' ) ) ;
if ( result . statusCode !== 201 ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , error ) ) ;
callback ( null ) ;
} ) ;
} ) ;
}
2018-01-17 12:26:53 -08:00
function doMigrate ( options , caasConfig , callback ) {
2017-12-09 08:08:15 +05:30
assert . strictEqual ( typeof options , 'object' ) ;
2018-01-17 12:26:53 -08:00
assert . strictEqual ( typeof caasConfig , 'object' ) ;
2017-12-09 08:08:15 +05:30
assert . strictEqual ( typeof callback , 'function' ) ;
var error = locker . lock ( locker . OP _MIGRATE ) ;
if ( error ) return callback ( new CaasError ( CaasError . BAD _STATE , error . message ) ) ;
function unlock ( error ) {
debug ( 'Failed to migrate' , error ) ;
locker . unlock ( locker . OP _MIGRATE ) ;
progress . set ( progress . MIGRATE , - 1 , 'Backup failed: ' + error . message ) ;
}
progress . set ( progress . MIGRATE , 10 , 'Backing up for migration' ) ;
// initiate the migration in the background
backups . backupBoxAndApps ( { userId : null , username : 'migrator' } , function ( error ) {
if ( error ) return unlock ( error ) ;
debug ( 'migrate: domain: %s size %s region %s' , options . domain , options . size , options . region ) ;
superagent
2018-01-17 12:26:53 -08:00
. post ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + caasConfig . boxId + '/migrate' )
. query ( { token : caasConfig . token } )
2017-12-09 08:08:15 +05:30
. send ( options )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return unlock ( error ) ; // network error
if ( result . statusCode === 409 ) return unlock ( new CaasError ( CaasError . BAD _STATE ) ) ;
if ( result . statusCode === 404 ) return unlock ( new CaasError ( CaasError . NOT _FOUND ) ) ;
if ( result . statusCode !== 202 ) return unlock ( new CaasError ( CaasError . EXTERNAL _ERROR , util . format ( '%s %j' , result . status , result . body ) ) ) ;
progress . set ( progress . MIGRATE , 10 , 'Migrating' ) ;
retire ( 'migrate' , _ . pick ( options , 'domain' , 'size' , 'region' ) ) ;
} ) ;
} ) ;
callback ( null ) ;
}
2017-12-09 08:42:15 +05:30
function changePlan ( options , callback ) {
assert . strictEqual ( typeof options , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
if ( config . isDemo ( ) ) return callback ( new CaasError ( CaasError . BAD _FIELD , 'Not allowed in demo mode' ) ) ;
2018-01-17 12:26:53 -08:00
getCaasConfig ( function ( error , result ) {
if ( error ) return callback ( error ) ;
doMigrate ( options , result , callback ) ;
} ) ;
2017-12-09 08:42:15 +05:30
}
2017-12-09 08:08:15 +05:30
// this function expects a lock
function upgrade ( boxUpdateInfo , callback ) {
assert ( boxUpdateInfo !== null && typeof boxUpdateInfo === 'object' ) ;
function upgradeError ( e ) {
progress . set ( progress . UPDATE , - 1 , e . message ) ;
callback ( e ) ;
}
progress . set ( progress . UPDATE , 5 , 'Backing up for upgrade' ) ;
backups . backupBoxAndApps ( { userId : null , username : 'upgrader' } , function ( error ) {
if ( error ) return upgradeError ( error ) ;
2018-01-17 12:26:53 -08:00
getCaasConfig ( function ( error , result ) {
if ( error ) return upgradeError ( error ) ;
2017-12-09 08:08:15 +05:30
2018-01-17 12:26:53 -08:00
superagent . post ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + result . boxId + '/upgrade' )
. query ( { token : result . token } )
. send ( { version : boxUpdateInfo . version } )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return upgradeError ( new Error ( 'Network error making upgrade request: ' + error ) ) ;
if ( result . statusCode !== 202 ) return upgradeError ( new Error ( util . format ( 'Server not ready to upgrade. statusCode: %s body: %j' , result . status , result . body ) ) ) ;
2017-12-09 08:08:15 +05:30
2018-01-17 12:26:53 -08:00
progress . set ( progress . UPDATE , 10 , 'Updating base system' ) ;
2017-12-09 08:08:15 +05:30
2018-01-17 12:26:53 -08:00
// no need to unlock since this is the last thing we ever do on this box
callback ( ) ;
retire ( 'upgrade' ) ;
} ) ;
} ) ;
2017-12-09 08:08:15 +05:30
} ) ;
}
2018-01-02 12:47:33 -08:00
function sendHeartbeat ( ) {
assert ( config . provider ( ) === 'caas' , 'Heartbeat is only sent for managed cloudrons' ) ;
2018-01-17 12:26:53 -08:00
getCaasConfig ( function ( error , result ) {
if ( error ) return debug ( 'Caas config missing' , error ) ;
var url = config . apiServerOrigin ( ) + '/api/v1/boxes/' + result . boxId + '/heartbeat' ;
superagent . post ( url ) . query ( { token : result . token , version : config . version ( ) } ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
if ( error && ! error . response ) debug ( 'Network error sending heartbeat.' , error ) ;
else if ( result . statusCode !== 200 ) debug ( 'Server responded to heartbeat with %s %s' , result . statusCode , result . text ) ;
else debug ( 'Heartbeat sent to %s' , url ) ;
} ) ;
2018-01-02 12:47:33 -08:00
} ) ;
}
2018-01-02 13:05:30 -08:00
function getBoxAndUserDetails ( callback ) {
assert . strictEqual ( typeof callback , 'function' ) ;
if ( config . provider ( ) !== 'caas' ) return callback ( null , { } ) ;
2018-01-17 15:22:38 -08:00
getCaasConfig ( function ( error , caasConfig ) {
if ( error ) return callback ( error ) ;
2018-01-02 13:05:30 -08:00
2018-01-17 15:22:38 -08:00
superagent
. get ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + caasConfig . boxId )
. query ( { token : caasConfig . token } )
. timeout ( 30 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , 'Cannot reach appstore' ) ) ;
if ( result . statusCode !== 200 ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , util . format ( '%s %j' , result . statusCode , result . body ) ) ) ;
2018-01-02 13:05:30 -08:00
2018-01-17 15:22:38 -08:00
return callback ( null , result . body ) ;
} ) ;
} ) ;
2018-01-02 13:05:30 -08:00
}
2018-01-01 19:19:07 -08:00
function setPtrRecord ( domain , callback ) {
assert . strictEqual ( typeof domain , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2018-01-17 12:26:53 -08:00
getCaasConfig ( function ( error , result ) {
if ( error ) return callback ( error ) ;
2018-01-01 19:19:07 -08:00
2018-01-17 12:26:53 -08:00
superagent
. post ( config . apiServerOrigin ( ) + '/api/v1/boxes/' + result . boxId + '/ptr' )
. query ( { token : result . token } )
. send ( { domain : domain } )
. timeout ( 5 * 1000 )
. end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , 'Cannot reach appstore' ) ) ;
if ( result . statusCode !== 202 ) return callback ( new CaasError ( CaasError . EXTERNAL _ERROR , util . format ( '%s %j' , result . statusCode , result . body ) ) ) ;
return callback ( null ) ;
} ) ;
} ) ;
2018-01-01 19:19:07 -08:00
}