2017-06-27 22:42:53 +03:00
2017-05-11 23:18:51 +03:00
'use strict' ;
2017-06-27 22:42:53 +03:00
// core modules
2017-06-02 00:25:45 +03:00
const EventEmitter = require ( 'events' ) ;
2017-06-27 22:42:53 +03:00
const Util = require ( 'util' ) ;
// 3rd party modules
2017-06-28 13:03:16 +03:00
// const CPClient = require('cp-client');
2017-06-03 01:16:49 +03:00
const DockerClient = require ( 'docker-compose-client' ) ;
2017-06-08 21:43:24 +03:00
const Dockerode = require ( 'dockerode' ) ;
2017-05-11 23:18:51 +03:00
const Hoek = require ( 'hoek' ) ;
2017-07-21 17:08:15 +03:00
const Boom = require ( 'boom' ) ;
2017-06-29 17:38:30 +03:00
const Triton = require ( 'triton' ) ;
2017-06-27 22:42:53 +03:00
const ParamCase = require ( 'param-case' ) ;
2017-05-11 23:18:51 +03:00
const Penseur = require ( 'penseur' ) ;
2017-06-27 22:42:53 +03:00
const UniqBy = require ( 'lodash.uniqby' ) ;
2017-07-05 16:33:16 +03:00
const Find = require ( 'lodash.find' ) ;
const Get = require ( 'lodash.get' ) ;
const Flatten = require ( 'lodash.flatten' ) ;
const ForceArray = require ( 'force-array' ) ;
2017-06-27 22:42:53 +03:00
const Uuid = require ( 'uuid/v4' ) ;
2017-05-27 19:35:38 +03:00
const VAsync = require ( 'vasync' ) ;
2017-06-27 22:42:53 +03:00
// local modules
2017-05-27 19:35:38 +03:00
const Transform = require ( './transform' ) ;
2017-07-11 19:59:25 +03:00
const { DEPLOYMENT _GROUP , SERVICE , HASH } = require ( '../watch/machines' ) ;
2017-06-26 17:29:12 +03:00
const NON _IMPORTABLE _STATES = [
'EXITED' ,
'DELETED' ,
'STOPPED' ,
'FAILED'
] ;
2017-05-11 23:18:51 +03:00
2017-07-05 16:33:16 +03:00
const NEW _INSTANCE _ID = '__NEW__' ;
const UNKNOWN _INSTANCE _ID = '__UNKNOWN__' ;
2017-05-11 23:18:51 +03:00
const internals = {
defaults : {
2017-05-25 23:03:39 +03:00
name : 'portal' ,
db : {
test : false
} ,
2017-06-08 21:43:24 +03:00
dockerComposeHost : 'tcp://0.0.0.0:4242'
2017-05-25 23:03:39 +03:00
} ,
2017-05-27 19:35:38 +03:00
tables : {
2017-06-05 23:54:44 +03:00
'portals' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'datacenters' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'deployment_groups' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'versions' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'manifests' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'services' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'packages' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'instances' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false } ,
'users' : { id : { type : 'uuid' } , primary : 'id' , secondary : false , purge : false }
2017-06-27 22:42:53 +03:00
} ,
resolveCb : ( resolve , reject ) => {
return ( err , ... args ) => {
if ( err ) {
return reject ( err ) ;
}
resolve ( ... args ) ;
} ;
2017-07-21 17:08:15 +03:00
} ,
fromKeyValueToDict : ( kv ) => {
return kv . reduce ( ( acc , { name , value } ) => {
return Object . assign ( acc , {
[ name ] : value
} ) ;
} , { } ) ;
} ,
isNotFound : ( err ) => {
return err && ( err [ 'typeof' ] === Boom . notFound ) ;
2017-05-27 19:35:38 +03:00
}
2017-05-11 23:18:51 +03:00
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
class Data extends EventEmitter {
2017-05-25 23:03:39 +03:00
constructor ( options ) {
2017-06-02 00:25:45 +03:00
super ( ) ;
2017-06-22 20:09:13 +03:00
2017-06-05 23:54:44 +03:00
const settings = Hoek . applyToDefaults ( internals . defaults , options || { } ) ;
2017-05-11 23:18:51 +03:00
// Penseur will assert that the options are correct
2017-05-25 23:03:39 +03:00
this . _db = new Penseur . Db ( settings . name , settings . db ) ;
2017-06-08 21:43:24 +03:00
this . _dockerCompose = new DockerClient ( settings . dockerComposeHost ) ;
this . _docker = new Dockerode ( settings . docker ) ;
2017-07-11 19:59:25 +03:00
this . _machines = null ;
2017-06-29 17:38:30 +03:00
this . _triton = null ;
2017-07-31 23:10:04 +03:00
this . _server = settings . server ;
2017-06-03 01:16:49 +03:00
2017-06-29 17:38:30 +03:00
Triton . createClient ( {
profile : settings . triton
} , ( err , client ) => {
if ( err ) {
this . emit ( 'error' , err ) ;
return ;
}
this . _triton = client . cloudapi ;
} ) ;
2017-06-27 22:42:53 +03:00
2017-06-08 21:43:24 +03:00
this . _dockerCompose . on ( 'error' , ( err ) => {
2017-06-03 01:16:49 +03:00
this . emit ( 'error' , err ) ;
} ) ;
2017-05-16 18:54:39 +03:00
}
2017-07-11 19:59:25 +03:00
setMachinesWatcher ( machinesWatcher ) {
this . _machines = machinesWatcher ;
2017-06-26 17:29:12 +03:00
}
2017-05-27 19:35:38 +03:00
connect ( cb ) {
this . _db . establish ( internals . tables , cb ) ;
}
2017-06-22 20:08:13 +03:00
reconnectCompose ( dockerComposeHost ) {
this . _dockerCompose . close ( ) ;
this . _dockerCompose = new DockerClient ( dockerComposeHost ) ;
this . _dockerCompose . on ( 'error' , ( err ) => {
this . emit ( 'error' , err ) ;
} ) ;
}
2017-05-27 19:35:38 +03:00
// portals
createPortal ( clientPortal , cb ) {
const portal = Transform . toPortal ( clientPortal ) ;
this . _db . portals . insert ( portal , ( err , key ) => {
if ( err ) {
return cb ( err ) ;
}
portal . id = key ;
cb ( null , Transform . fromPortal ( { portal } ) ) ;
} ) ;
}
2017-06-05 23:54:44 +03:00
getPortal ( options , cb ) {
2017-05-27 19:35:38 +03:00
this . _db . portals . all ( ( err , portals ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-05 23:54:44 +03:00
if ( ! portals ) {
return cb ( ) ;
}
2017-06-22 20:09:13 +03:00
const portal = portals . shift ( ) ;
2017-05-25 23:03:39 +03:00
2017-06-22 20:09:13 +03:00
// Sub query/filter for deploymentGroups
2017-07-05 16:33:16 +03:00
const deploymentGroups = ( args , cb ) => {
if ( typeof cb === 'function' ) {
return this . getDeploymentGroups ( args , cb ) ;
}
2017-06-22 20:09:13 +03:00
return new Promise ( ( resolve , reject ) => {
2017-06-27 22:42:53 +03:00
this . getDeploymentGroups ( args , internals . resolveCb ( resolve , reject ) ) ;
2017-06-22 20:09:13 +03:00
} ) ;
} ;
2017-06-08 00:35:45 +03:00
2017-06-22 20:09:13 +03:00
// Sub query/filter for user
const user = ( ) => {
return new Promise ( ( resolve , reject ) => {
2017-06-27 22:42:53 +03:00
this . getUser ( { } , internals . resolveCb ( resolve , reject ) ) ;
2017-06-22 20:09:13 +03:00
} ) ;
} ;
2017-06-08 00:35:45 +03:00
2017-06-22 20:09:13 +03:00
// Sub query/filter for datacenter
const datacenter = ( ) => {
return new Promise ( ( resolve , reject ) => {
2017-06-27 22:42:53 +03:00
this . getDatacenter ( { id : portal . datacenter _id } , internals . resolveCb ( resolve , reject ) ) ;
2017-06-22 20:09:13 +03:00
} ) ;
} ;
cb ( null , Transform . fromPortal ( {
portal ,
deploymentGroups ,
datacenter ,
user
} ) ) ;
2017-05-25 23:03:39 +03:00
} ) ;
2017-05-16 18:54:39 +03:00
}
2017-05-27 19:35:38 +03:00
// datacenters
2017-05-11 23:18:51 +03:00
2017-05-27 19:35:38 +03:00
createDatacenter ( datacenter , cb ) {
this . _db . datacenters . insert ( datacenter , ( err , key ) => {
if ( err ) {
return cb ( err ) ;
}
2017-05-11 23:18:51 +03:00
2017-05-27 19:35:38 +03:00
datacenter . id = key ;
cb ( null , datacenter ) ;
} ) ;
2017-05-11 23:18:51 +03:00
}
2017-05-27 19:35:38 +03:00
getDatacenters ( cb ) {
this . _db . datacenters . all ( cb ) ;
2017-05-11 23:18:51 +03:00
}
2017-05-27 19:35:38 +03:00
getDatacenter ( { id , region } , cb ) {
Hoek . assert ( id || region , 'id or region are required to retrieve a datacenter' ) ;
2017-05-11 23:18:51 +03:00
2017-05-27 19:35:38 +03:00
if ( region ) {
2017-06-05 23:54:44 +03:00
return this . _db . datacenters . query ( { region } , ( err , datacenters ) => {
if ( err ) {
return cb ( err ) ;
}
return cb ( null , datacenters && datacenters . length ? datacenters [ 0 ] : null ) ;
} ) ;
2017-05-27 19:35:38 +03:00
}
2017-06-05 23:54:44 +03:00
this . _db . datacenters . get ( id , cb ) ;
}
// users
createUser ( clientUser , cb ) {
const user = Transform . toUser ( clientUser ) ;
this . _db . users . insert ( user , ( err , key ) => {
if ( err ) {
return cb ( err ) ;
}
user . id = key ;
cb ( null , Transform . fromUser ( user ) ) ;
} ) ;
}
getUser ( options , cb ) {
this . _db . users . all ( ( err , users ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! users || ! users . length ) {
return cb ( ) ;
}
cb ( null , Transform . fromUser ( users [ 0 ] ) ) ;
} ) ;
2017-05-11 23:18:51 +03:00
}
2017-05-27 19:35:38 +03:00
// deployment_groups
2017-05-31 01:08:06 +03:00
createDeploymentGroup ( clientDeploymentGroup , cb ) {
2017-07-20 19:31:42 +03:00
const dg = Transform . toDeploymentGroup ( clientDeploymentGroup ) ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating DeploymentGroup: ${ Util . inspect ( dg ) } ` ) ;
2017-07-20 19:31:42 +03:00
2017-06-22 20:09:13 +03:00
this . _db . deployment _groups . query ( {
2017-07-20 19:31:42 +03:00
slug : dg . slug
} , ( err , dgs ) => {
2017-05-27 19:35:38 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-20 19:31:42 +03:00
if ( dgs && dgs . length ) {
return cb ( new Error ( ` DeploymentGroup " ${ dg . slug } " already exists ( ${ dgs [ 0 ] . id } ) ` ) ) ;
2017-06-22 20:09:13 +03:00
}
2017-07-20 19:31:42 +03:00
this . _db . deployment _groups . insert ( dg , ( err , key ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-20 19:31:42 +03:00
dg . id = key ;
cb ( null , Transform . fromDeploymentGroup ( dg ) ) ;
2017-06-22 20:09:13 +03:00
} ) ;
2017-05-11 23:18:51 +03:00
} ) ;
}
2017-07-05 16:33:16 +03:00
updateDeploymentGroup ( clientDeploymentGroup , cb ) {
2017-07-20 19:31:42 +03:00
const dg = Transform . toDeploymentGroup ( clientDeploymentGroup ) ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> updating DeploymentGroup: ${ Util . inspect ( dg ) } ` ) ;
2017-07-20 19:31:42 +03:00
this . _db . deployment _groups . update ( [ dg ] , ( err ) => {
2017-05-27 19:35:38 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-05-11 23:18:51 +03:00
2017-07-05 16:33:16 +03:00
this . getDeploymentGroup ( { id : clientDeploymentGroup . id } , cb ) ;
2017-05-27 19:35:38 +03:00
} ) ;
2017-05-12 22:59:37 +03:00
}
2017-07-05 16:33:16 +03:00
_getDeploymentGroupFns ( deploymentGroup ) {
const getServices = ( args , cb ) => {
2017-06-27 18:15:27 +03:00
args = args || { } ;
2017-07-05 16:33:16 +03:00
args . ids = deploymentGroup . service _ids ;
if ( typeof cb === 'function' ) {
2017-07-20 19:31:42 +03:00
return args . ids && args . ids . length ?
this . getServices ( args , cb ) :
cb ( null , [ ] ) ;
2017-07-05 16:33:16 +03:00
}
2017-06-27 18:15:27 +03:00
return new Promise ( ( resolve , reject ) => {
2017-07-20 19:31:42 +03:00
return args . ids && args . ids . length ?
this . getServices ( args , internals . resolveCb ( resolve , reject ) ) :
resolve ( [ ] ) ;
2017-06-27 18:15:27 +03:00
} ) ;
} ;
2017-07-05 16:33:16 +03:00
const getVersion = ( args , cb ) => {
2017-06-27 18:15:27 +03:00
args = args || { } ;
args . id = deploymentGroup . version _id ;
2017-07-05 16:33:16 +03:00
if ( typeof cb === 'function' ) {
return deploymentGroup . version _id ?
this . getVersion ( args , cb ) :
cb ( null ) ;
}
2017-06-27 18:15:27 +03:00
return new Promise ( ( resolve , reject ) => {
2017-06-27 20:47:44 +03:00
return deploymentGroup . version _id ?
2017-06-28 02:11:19 +03:00
this . getVersion ( args , internals . resolveCb ( resolve , reject ) ) :
2017-06-27 20:47:44 +03:00
resolve ( null ) ;
2017-06-27 18:15:27 +03:00
} ) ;
} ;
2017-07-05 16:33:16 +03:00
const getHistory = ( args , cb ) => {
args = args || { } ;
args . version _ids = ForceArray ( deploymentGroup . history _version _ids ) ;
if ( typeof cb === 'function' ) {
2017-07-20 19:31:42 +03:00
return args . version _ids && args . version _ids . length ?
this . getHistory ( args , cb ) :
cb ( null , [ ] ) ;
2017-07-05 16:33:16 +03:00
}
return new Promise ( ( resolve , reject ) => {
2017-07-20 19:31:42 +03:00
return args . version _ids && args . version _ids . length ?
this . getHistory ( args , internals . resolveCb ( resolve , reject ) ) :
resolve ( [ ] ) ;
2017-07-05 16:33:16 +03:00
} ) ;
} ;
2017-06-27 18:15:27 +03:00
return Object . assign ( deploymentGroup , {
services : getServices ,
2017-07-05 16:33:16 +03:00
version : getVersion ,
history : getHistory
2017-06-27 18:15:27 +03:00
} ) ;
}
2017-05-31 01:08:06 +03:00
getDeploymentGroups ( { ids , name , slug } , cb ) {
const finish = ( err , deploymentGroups ) => {
2017-05-27 19:35:38 +03:00
if ( err ) {
return cb ( err ) ;
2017-05-16 18:54:39 +03:00
}
2017-05-11 23:18:51 +03:00
2017-07-21 17:08:15 +03:00
if ( ( ids || name || slug ) && ( ! deploymentGroups || ! deploymentGroups . length ) ) {
return cb ( Boom . notFound ( ) ) ;
}
2017-06-27 18:15:27 +03:00
if ( ! deploymentGroups || ! deploymentGroups . length ) {
return cb ( null , [ ] ) ;
}
2017-07-05 16:33:16 +03:00
cb ( null , deploymentGroups . map ( ( dg ) => {
return Transform . fromDeploymentGroup ( this . _getDeploymentGroupFns ( dg ) ) ;
} ) ) ;
2017-05-31 01:08:06 +03:00
} ;
if ( ids ) {
return this . _db . deployment _groups . get ( ids , finish ) ;
}
if ( name ) {
return this . _db . deployment _groups . query ( { name } , finish ) ;
}
2017-06-05 23:54:44 +03:00
if ( slug ) {
return this . _db . deployment _groups . query ( { slug } , finish ) ;
}
2017-06-08 00:35:45 +03:00
return this . _db . deployment _groups . all ( finish ) ;
2017-05-11 23:18:51 +03:00
}
2017-05-12 22:59:37 +03:00
2017-05-31 01:08:06 +03:00
getDeploymentGroup ( query , cb ) {
2017-06-22 20:09:13 +03:00
query = query || { } ;
2017-06-09 21:13:22 +03:00
this . _db . deployment _groups . query ( query , ( err , deploymentGroups ) => {
2017-06-08 00:35:45 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-03 01:16:49 +03:00
2017-06-09 21:13:22 +03:00
if ( ! deploymentGroups || ! deploymentGroups . length ) {
2017-07-21 17:08:15 +03:00
return cb ( Boom . notFound ( ) ) ;
2017-06-08 00:35:45 +03:00
}
2017-05-27 19:35:38 +03:00
2017-07-05 16:33:16 +03:00
cb ( null , Transform . fromDeploymentGroup ( this . _getDeploymentGroupFns ( deploymentGroups [ 0 ] ) ) ) ;
2017-06-27 18:15:27 +03:00
} ) ;
}
2017-06-08 00:35:45 +03:00
2017-07-17 17:19:31 +03:00
deleteDeploymentGroup ( { id } , cb ) {
// dg, services, instances, versions, manifests
const remove = ( err , result ) => {
if ( err ) {
return cb ( err ) ;
}
const res = ForceArray ( result . successes ) . reduce ( ( acc , res ) => {
return Object . assign ( acc , res ) ;
} , { } ) ;
VAsync . parallel ( {
funcs : [
( cb ) => {
2017-07-21 17:08:15 +03:00
if ( ! res . dg ) {
return cb ( ) ;
}
2017-07-17 17:19:31 +03:00
this . _db . deployment _groups . remove ( { id } , cb ) ;
} ,
( cb ) => {
2017-07-21 17:08:15 +03:00
if ( ! res . services ) {
return cb ( ) ;
}
2017-07-17 17:19:31 +03:00
VAsync . forEachParallel ( {
inputs : res . services ,
func : ( { id } , next ) => {
this . _db . services . remove ( { id } , next ) ;
}
} ) ;
} ,
( cb ) => {
2017-07-21 17:08:15 +03:00
if ( ! res . instances ) {
return cb ( ) ;
}
2017-07-17 17:19:31 +03:00
VAsync . forEachParallel ( {
inputs : res . instances ,
func : ( { id } , next ) => {
this . _db . instances . remove ( { id } , next ) ;
}
} ) ;
} ,
( cb ) => {
VAsync . forEachParallel ( {
inputs : res . versions ,
func : ( { id } , next ) => {
this . _db . versions . remove ( { id } , next ) ;
}
} ) ;
} ,
( cb ) => {
VAsync . forEachParallel ( {
inputs : res . manifests ,
func : ( { id } , next ) => {
this . _db . manifests . remove ( { id } , next ) ;
}
} ) ;
}
]
} , ( err ) => {
cb ( err , res . cb ) ;
} ) ;
} ;
VAsync . parallel ( {
funcs : [
( cb ) => {
this . getDeploymentGroup ( { id } , ( err , dg ) => {
2017-07-21 17:08:15 +03:00
if ( internals . isNotFound ( err ) ) {
return cb ( null , { } ) ;
}
2017-07-17 17:19:31 +03:00
cb ( err , { dg } ) ;
} ) ;
} ,
( cb ) => {
this . getServices ( { deploymentGroupId : id } , ( err , services ) => {
2017-07-21 17:08:15 +03:00
if ( internals . isNotFound ( err ) ) {
return cb ( null , { } ) ;
}
2017-07-17 17:19:31 +03:00
cb ( err , { services } ) ;
} ) ;
} ,
( cb ) => {
this . getInstances ( { deploymentGroupId : id } , ( err , instances ) => {
2017-07-21 17:08:15 +03:00
if ( internals . isNotFound ( err ) ) {
return cb ( null , { } ) ;
}
2017-07-17 17:19:31 +03:00
cb ( err , { instances } ) ;
} ) ;
} ,
( cb ) => {
this . getVersions ( { deploymentGroupId : id } , ( err , versions ) => {
cb ( err , { versions } ) ;
} ) ;
} ,
( cb ) => {
this . getManifests ( { deploymentGroupId : id } , ( err , manifests ) => {
cb ( err , { manifests } ) ;
} ) ;
}
]
} , remove ) ;
}
2017-07-05 16:33:16 +03:00
// versions
_versionFns ( version ) {
2017-06-27 18:15:27 +03:00
return Object . assign ( version , {
manifest : ( args ) => {
2017-06-22 20:09:13 +03:00
return new Promise ( ( resolve , reject ) => {
2017-06-27 18:15:27 +03:00
return this . getManifest ( {
id : version . manifest _id
2017-06-28 02:11:19 +03:00
} , internals . resolveCb ( resolve , reject ) ) ;
2017-06-03 01:16:49 +03:00
} ) ;
2017-06-27 18:15:27 +03:00
}
2017-05-27 19:35:38 +03:00
} ) ;
2017-05-16 18:54:39 +03:00
}
2017-05-11 23:18:51 +03:00
2017-05-27 19:35:38 +03:00
createVersion ( clientVersion , cb ) {
2017-06-02 00:25:45 +03:00
Hoek . assert ( clientVersion , 'version is required' ) ;
2017-06-27 18:15:27 +03:00
Hoek . assert ( clientVersion . manifest , 'manifest is required' ) ;
2017-06-02 00:25:45 +03:00
Hoek . assert ( clientVersion . deploymentGroupId , 'deploymentGroupId is required' ) ;
2017-05-31 01:08:06 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating new Version for DeploymentGroup ${ clientVersion . deploymentGroupId } : ${ Util . inspect ( clientVersion ) } ` ) ;
2017-06-22 20:09:13 +03:00
2017-06-02 00:25:45 +03:00
const version = Transform . toVersion ( clientVersion ) ;
this . _db . versions . insert ( version , ( err , key ) => {
2017-05-27 19:35:38 +03:00
if ( err ) {
return cb ( err ) ;
2017-05-16 18:54:39 +03:00
}
2017-05-12 22:59:37 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> new Version for DeploymentGroup ${ clientVersion . deploymentGroupId } created: ${ key } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . deployment _groups . query ( {
id : clientVersion . deploymentGroupId
} , ( err , deploymentGroup ) => {
2017-05-31 01:08:06 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
const changes = {
id : clientVersion . deploymentGroupId ,
version _id : key ,
history _version _ids : deploymentGroup . version _id ?
ForceArray ( deploymentGroup . history _version _ids ) . concat ( [ ] ) :
[ ]
} ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> updating DeploymentGroup ${ clientVersion . deploymentGroupId } to add Version ${ key } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . deployment _groups . update ( [ changes ] , ( err ) => {
if ( err ) {
return cb ( err ) ;
}
this . getVersion ( { id : key } , cb ) ;
} ) ;
2017-05-31 01:08:06 +03:00
} ) ;
2017-05-11 23:18:51 +03:00
} ) ;
}
2017-05-12 22:59:37 +03:00
2017-06-22 20:09:13 +03:00
updateVersion ( clientVersion , cb ) {
2017-07-05 16:33:16 +03:00
this . _db . versions . update ( [ Transform . toVersion ( clientVersion ) ] , ( err ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
this . getVersion ( { id : clientVersion . id } , cb ) ;
2017-06-22 20:09:13 +03:00
} ) ;
}
2017-05-31 01:08:06 +03:00
getVersion ( { id , manifestId } , cb ) {
const query = id ? { id } : { manifest _id : manifestId } ;
this . _db . versions . single ( query , ( err , version ) => {
2017-05-27 19:35:38 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-27 18:15:27 +03:00
if ( ! version ) {
return cb ( null , null ) ;
}
2017-07-05 16:33:16 +03:00
cb ( null , Transform . fromVersion ( this . _versionFns ( version ) ) ) ;
2017-05-27 19:35:38 +03:00
} ) ;
2017-05-12 22:59:37 +03:00
}
2017-05-31 01:08:06 +03:00
getVersions ( { manifestId , deploymentGroupId } , cb ) {
const finish = ( err , versions ) => {
if ( err ) {
return cb ( err ) ;
}
versions = versions || [ ] ;
2017-07-05 16:33:16 +03:00
cb ( null , versions . map ( ( version ) => {
return Transform . fromVersion ( this . _versionFns ( version ) ) ;
} ) ) ;
2017-05-31 01:08:06 +03:00
} ;
2017-06-02 00:25:45 +03:00
// ensure the data is in sync
this . _db . versions . sync ( ( ) => {
if ( manifestId ) {
return this . _db . versions . query ( { manifest _id : manifestId } , finish ) ;
2017-05-31 01:08:06 +03:00
}
2017-06-02 00:25:45 +03:00
this . getDeploymentGroup ( { id : deploymentGroupId } , ( err , deploymentGroup ) => {
if ( err ) {
return finish ( err ) ;
}
this . _db . versions . get ( deploymentGroup . history , finish ) ;
} ) ;
2017-05-31 01:08:06 +03:00
} ) ;
}
2017-07-05 16:33:16 +03:00
getHistory ( { version _ids } , cb ) {
this . _db . services . get ( version _ids , ( err , versions ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! versions || ! versions . length ) {
return cb ( null , [ ] ) ;
}
cb ( null , versions . map ( ( version ) => {
return Transform . fromVersion ( this . _versionFns ( version ) ) ;
} ) ) ;
} ) ;
}
_calcCurrentScale ( { config , currentVersion } , cb ) {
return config . map ( ( { name } ) => {
const currentScale = Find ( ForceArray ( currentVersion . scale ) , [
'serviceName' ,
name
] ) ;
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
serviceName : name ,
replicas : Number . isFinite ( currentScale ) ? currentScale : 1
} ;
} ) ;
}
_getCurrentScale ( { deploymentGroupName , config , currentVersion } , cb ) {
const fallback = ( err ) => {
if ( err ) {
console . error ( err ) ;
}
this . _calcCurrentScale ( { config , currentVersion } , cb ) ;
} ;
if ( ! this . _triton ) {
return fallback ( ) ;
}
2017-07-20 19:31:42 +03:00
const machines = ForceArray ( this . _machines . getContainers ( ) )
. filter ( ( { tags = { } } ) => {
return tags [ DEPLOYMENT _GROUP ] === deploymentGroupName ;
} ) ;
2017-07-05 16:33:16 +03:00
2017-07-20 19:31:42 +03:00
const liveServices = machines . reduce ( ( acc , { tags } ) => {
return Object . assign ( acc , {
[ tags [ SERVICE ] ] : 1
} ) ;
} , { } ) ;
2017-07-05 16:33:16 +03:00
2017-07-20 19:31:42 +03:00
const allAndConfigServices = config . reduce ( ( acc , { name } ) => {
return Object . assign ( acc , {
[ name ] : 1
} ) ;
} , liveServices ) ;
2017-07-05 16:33:16 +03:00
2017-07-20 19:31:42 +03:00
const scale = Object . keys ( allAndConfigServices ) . map ( ( name ) => {
const existingMachines = machines . filter ( ( machine ) => {
return machine . tags [ SERVICE ] === name ;
2017-07-05 16:33:16 +03:00
} ) ;
2017-07-20 19:31:42 +03:00
return {
id : Uuid ( ) ,
serviceName : name ,
replicas : existingMachines . length ? existingMachines . length : 1
} ;
} ) ;
2017-07-05 16:33:16 +03:00
2017-07-20 19:31:42 +03:00
cb ( null , scale ) ;
2017-07-05 16:33:16 +03:00
}
2017-07-15 06:38:27 +03:00
scale ( { serviceId , replicas } , cb ) {
2017-06-28 13:04:43 +03:00
Hoek . assert ( serviceId , 'service id is required' ) ;
2017-06-03 01:16:49 +03:00
Hoek . assert ( typeof replicas === 'number' && replicas >= 0 , 'replicas must be a number no less than 0' ) ;
2017-07-05 16:33:16 +03:00
// get the service
// check service status
// update service status
// get the deployment group
2017-06-03 01:16:49 +03:00
// use the deployment group to find the current version and manifest
2017-07-05 16:33:16 +03:00
// get instances
// get current scale
// identify plan and future scale
// update version
// callback
2017-06-03 01:16:49 +03:00
// scale the service
2017-07-05 16:33:16 +03:00
// this._scale({ service, deployment_group, version, manifest, replicas }, cb);
const ctx = {
isHandled : false
} ;
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> scale request received' ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const handleFailedScale = ( err1 , cb ) => {
if ( err1 ) {
console . error ( err1 ) ;
}
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , ( err2 ) => {
if ( err2 ) {
console . error ( err2 ) ;
}
if ( typeof cb === 'function' ) {
cb ( err1 || err2 ) ;
}
} ) ;
} ;
const handleTriggeredScale = ( err ) => {
2017-06-03 01:16:49 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return handleFailedScale ( err ) ;
2017-06-03 01:16:49 +03:00
}
2017-07-05 16:33:16 +03:00
if ( ctx . isHandled ) {
return ;
2017-06-03 01:16:49 +03:00
}
2017-07-05 16:33:16 +03:00
ctx . isHandled = true ;
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got response from docker-compose to scale ${ ctx . service . name } to ${ replicas } replicas ` ) ;
2017-07-05 16:33:16 +03:00
} ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const triggerScale = ( err , newVersion ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
2017-06-03 01:16:49 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> new Version created' ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
cb ( null , newVersion ) ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
setImmediate ( ( ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> requesting docker-compose to scale ${ ctx . service . name } to ${ replicas } replicas ` ) ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
this . _dockerCompose . scale ( {
projectName : ctx . deploymentGroup . name ,
2017-07-15 06:38:27 +03:00
environment : ctx . manifest . environment ,
2017-07-21 17:08:15 +03:00
files : internals . fromKeyValueToDict ( ctx . manifest . files ) ,
2017-07-15 06:38:27 +03:00
manifest : ctx . manifest . raw ,
2017-07-05 16:33:16 +03:00
services : {
[ ctx . service . name ] : replicas
2017-07-15 06:38:27 +03:00
}
2017-07-05 16:33:16 +03:00
} , handleTriggeredScale ) ;
} ) ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const getNewScale = ( ) => {
return ctx . currentScale . map ( ( { serviceName , replicas } ) => {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
serviceName : serviceName ,
replicas : serviceName === ctx . service . name ?
( ctx . serviceScale + ctx . diff ) :
replicas
} ;
} ) ;
} ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const handleScaleDown = ( ) => {
const payload = {
manifest : ctx . manifest ,
deploymentGroupId : ctx . deploymentGroup . id ,
scale : getNewScale ( ) ,
plan : [ {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'REMOVE' ,
service : ctx . service . name ,
toProcess : Math . abs ( ctx . diff ) ,
machines : ctx . instances . map ( ( { machineId } ) => {
return machineId ;
} )
} ] ,
hasPlan : true
} ;
2017-06-03 01:16:49 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating new Version for DOWN scale ${ Util . inspect ( payload ) } ` ) ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
// note: createVersion updates deploymentGroup
this . createVersion ( payload , triggerScale ) ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const handleScaleUp = ( ) => {
const payload = {
manifest : ctx . manifest ,
deploymentGroupId : ctx . deploymentGroup . id ,
scale : getNewScale ( ) ,
plan : [ {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'CREATE' ,
service : ctx . service . name ,
toProcess : Math . abs ( ctx . diff ) ,
machines : ctx . instances . map ( ( { machineId } ) => {
return machineId ;
} )
} ] ,
hasPlan : true
} ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating new Version for UP scale ${ Util . inspect ( payload ) } ` ) ;
2017-07-05 16:33:16 +03:00
// note: createVersion updates deploymentGroup
this . createVersion ( payload , triggerScale ) ;
} ;
const handleCurrentScale = ( err , currentScale ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
2017-06-03 01:16:49 +03:00
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got current scale ${ Util . inspect ( currentScale ) } ` ) ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const serviceReplicas = Find ( currentScale , [ 'serviceName' , ctx . service . name ] ) . replicas ;
const serviceScale = Number . isFinite ( serviceReplicas ) ? serviceReplicas : 1 ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const diff = replicas - serviceScale ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
if ( diff === 0 ) {
return handleFailedScale ( null , cb ) ;
2017-06-22 20:09:13 +03:00
}
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
ctx . serviceScale = serviceScale ;
ctx . serviceReplicas = serviceReplicas ;
ctx . currentScale = currentScale ;
ctx . diff = diff ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
return ( diff > 0 ) ?
handleScaleUp ( ) :
handleScaleDown ( ) ;
} ;
const handleManifest = ( err , manifest ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
if ( ! manifest ) {
return cb ( new Error ( ` manifest not found for service with service id: ${ serviceId } ` ) ) ;
}
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
ctx . manifest = manifest ;
2017-06-03 01:16:49 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> fetching current scale' ) ;
2017-07-05 16:33:16 +03:00
this . _getCurrentScale ( {
deploymentGroupName : ctx . deploymentGroup . name ,
currentVersion : ctx . version ,
config : [ {
name : ctx . service . name
} ]
} , handleCurrentScale ) ;
} ;
const handleVersion = ( err , version ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
if ( ! version ) {
return cb ( new Error ( ` Version not found for service with service id: ${ serviceId } ` ) ) ;
}
ctx . version = version ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching Manifest ${ version . manifest _id } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . manifests . single ( {
id : version . manifest _id
} , handleManifest ) ;
} ;
const handleDeploymentGroup = ( err , deploymentGroup ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
if ( ! deploymentGroup ) {
return cb ( new Error ( ` deployment group not found for service with service id: ${ serviceId } ` ) ) ;
}
ctx . deploymentGroup = deploymentGroup ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching Version ${ ctx . deploymentGroup . version _id } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . versions . single ( {
id : deploymentGroup . version _id
} , handleVersion ) ;
} ;
const handleInstances = ( err , instances = [ ] ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got ${ instances . length } Instances from ${ ctx . service . name } ` ) ;
2017-07-05 16:33:16 +03:00
ctx . instances = instances ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching DeploymentGroup ${ ctx . service . deployment _group _id } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . deployment _groups . single ( {
id : ctx . service . deployment _group _id
} , handleDeploymentGroup ) ;
2017-06-03 01:16:49 +03:00
} ;
2017-07-05 16:33:16 +03:00
const handleUpdatedService = ( err ) => {
if ( err ) {
return handleFailedScale ( err , cb ) ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching Instances from ${ ctx . service . name } ` ) ;
2017-07-05 16:33:16 +03:00
this . getInstances ( { ids : ctx . service . instance _ids } , handleInstances ) ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const handleService = ( err , service ) => {
2017-06-03 01:16:49 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
if ( ! service ) {
return cb ( new Error ( ` Service not found for id: ${ serviceId } ` ) ) ;
}
if ( service . status !== 'ACTIVE' ) {
return cb ( new Error ( ` Can't scale when the status is " ${ service . status } " ` ) ) ;
}
ctx . service = service ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching DeploymentGroup ${ service . deployment _group _id } ` ) ;
2017-07-05 16:33:16 +03:00
this . updateService ( {
id : serviceId ,
status : 'SCALING'
} , handleUpdatedService ) ;
} ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching Service ${ serviceId } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . services . single ( { id : serviceId } , handleService ) ;
2017-06-03 01:16:49 +03:00
}
2017-05-31 01:08:06 +03:00
// manifests
provisionManifest ( clientManifest , cb ) {
2017-06-22 20:09:13 +03:00
// 1. check that the deploymentgroup exists
2017-07-05 16:33:16 +03:00
// 2. update Deployment Group to set PROVISIONING status
// 3. get docker-compose config for the given manifest
// 4. create a new manifest
// 5. fetch current version
// 6. get curent scale based on machines in triton
// 7. create new version
// 8. call `up` and get the response
// 9. iterate over services from provision response
// 10. on each service, either create or update it with new status and hash
// 11. fetch all the existing services
// 12. for each existing service, check if it still exists on this provision. if it doesn't update to mark DELETING
// 13. update deployment group with calculated plan and ACTIVE status
const ctx = {
isHandled : false
} ;
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> provision request received' ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const handleFailedProvision = ( err ) => {
if ( ! err ) {
return ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
console . error ( err ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
this . updateVersion ( {
id : ctx . newVersion . id ,
error : ` ${ err . message } \n ${ err . stack } `
} , ( err ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
console . error ( err ) ;
2017-06-22 20:09:13 +03:00
}
2017-07-05 16:33:16 +03:00
} ) ;
} ;
const ServiceStatusFromPlan = {
NOOP : 'ACTIVE' ,
CREATE : 'PROVISIONING' ,
RECREATE : 'PROVISIONING' ,
START : 'PROVISIONING'
} ;
// 15. handle fetched instantes
// 16. update deployment group with calculated plan and ACTIVE status
const handleServiceInstanceMap = ( err , result ) => {
if ( err ) {
return handleFailedProvision ( err ) ;
}
const services = ForceArray ( result . successes ) ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got a map of Service's-Instance's from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ${ Util . inspect ( services ) } ` ) ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
const plan = Flatten ( services . map ( ( { name , instances } ) => {
const provision = ctx . provisionRes [ name ] ;
const machines = instances . map ( ( { machineId } ) => {
return machineId ;
} ) ;
const { replicas } = Find ( ctx . currentScale , [ 'serviceName' , name ] ) ;
const scale = Number . isFinite ( replicas ) ? replicas : 1 ;
const action = Get ( provision , 'plan.action' , 'NOOP' ) . toUpperCase ( ) ;
if ( ! provision ) {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'REMOVE' ,
service : name ,
toProcess : machines . length ,
machines : machines
} ;
2017-06-22 20:09:13 +03:00
}
2017-07-05 16:33:16 +03:00
const ActionMap = {
'NOOP' : ( ) => {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'NOOP' ,
service : name ,
machines
} ;
} ,
'CREATE' : ( ) => {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'CREATE' ,
service : name ,
toProcess : scale ,
machines : machines
} ;
} ,
'RECREATE' : ( ) => {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'CREATE' ,
service : name ,
toProcess : machines . length ,
machines : machines
} ;
} ,
'START' : ( ) => {
return {
2017-07-05 16:33:16 +03:00
id : Uuid ( ) ,
2017-07-05 16:33:16 +03:00
type : 'START' ,
service : name ,
machines
} ;
}
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
return ActionMap [ action ] ( ) ;
} ) ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
VAsync . parallel ( {
funcs : [
( cb ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> updating Version ${ ctx . newVersion . id } from DeploymentGroup ${ ctx . currentDeploymentGroup . id } with new Plan ${ Util . inspect ( plan ) } ` ) ;
2017-07-05 16:33:16 +03:00
return this . updateVersion ( {
id : ctx . newVersion . id ,
hasPlan : true ,
plan
} , cb ) ;
} ,
( cb ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> updating DeploymentGroup ${ ctx . currentDeploymentGroup . id } with new Service's ${ Util . inspect ( ctx . newServices ) } and ACTIVE status ` ) ;
2017-07-05 16:33:16 +03:00
const services = UniqBy (
ForceArray ( ctx . newServices )
. concat ( ForceArray ( ctx . previousServices ) ) ,
'id'
) ;
this . updateDeploymentGroup ( {
id : ctx . currentDeploymentGroup . id ,
status : 'ACTIVE' ,
services : services
} , cb ) ;
2017-06-22 20:09:13 +03:00
}
2017-07-05 16:33:16 +03:00
]
} , handleFailedProvision ) ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
// 14. fetch instanceIds for each Service
const handleRemovedServices = ( err ) => {
if ( err ) {
return handleFailedProvision ( err ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> marked removed Service's with DELETING from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
this . _server . log ( [ 'debug' ] , ` -> fetching a map of Service's-Instance's from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
VAsync . forEachParallel ( {
inputs : ctx . previousServices ,
func : ( service , next ) => {
service . instances ( { } , ( err , instances ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return next ( err ) ;
2017-06-22 20:09:13 +03:00
}
2017-07-05 16:33:16 +03:00
next ( err , Object . assign ( { } , service , {
instances
} ) ) ;
2017-06-22 20:09:13 +03:00
} ) ;
2017-07-05 16:33:16 +03:00
}
} , handleServiceInstanceMap ) ;
2017-06-22 20:09:13 +03:00
} ;
2017-07-05 16:33:16 +03:00
// 13. handle all the existing services response
const handlePreviousServices = ( err , previousServices = [ ] ) => {
if ( err ) {
return handleFailedProvision ( err ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> identified previous Service's from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ${ Util . inspect ( ctx . previousServices ) } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
ctx . previousServices = previousServices ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
// 12. for existing service, check if it still exists on this provision. if it doesn't update to mark DELETING
ctx . removedServices = previousServices . filter ( ( { name } ) => {
return ! Find ( ctx . newServices , [ 'name' , name ] ) ;
2017-06-22 20:09:13 +03:00
} ) ;
2017-07-05 16:33:16 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> identified removed Service's from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ${ Util . inspect ( ctx . removedServices ) } ` ) ;
2017-07-05 16:33:16 +03:00
VAsync . forEachParallel ( {
inputs : ctx . removedServices ,
func : ( { id , name } , next ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> marking Service ${ name } as DELETING from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
this . updateService ( {
id ,
status : 'DELETING'
} , next ) ;
}
} , handleRemovedServices ) ;
2017-06-22 20:09:13 +03:00
} ;
2017-07-05 16:33:16 +03:00
// 11. fetch all the existing services
const handleNewServices = ( err , result ) => {
2017-05-31 01:08:06 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return handleFailedProvision ( err ) ;
2017-05-31 01:08:06 +03:00
}
2017-07-05 16:33:16 +03:00
ctx . newServices = ForceArray ( result . successes ) ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got " ${ ctx . newServices . length } " Services provisioned from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
ctx . currentDeploymentGroup . services ( { } , handlePreviousServices ) ;
} ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const createProvisionService = ( { payload } , cb ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating Service " ${ payload . name } " from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
this . createService ( payload , cb ) ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const updateProvisionService = ( { payload , serviceId } , cb ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> updating Service " ${ payload . name } " from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
this . updateService ( Object . assign ( { } , payload , {
id : serviceId
} ) , cb ) ;
} ;
// 10. on each service, either create or update it with new status and hash
const handleProvisionService = ( serviceName , next ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> handling Service " ${ serviceName } " from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
this . getServices ( {
name : serviceName ,
deploymentGroupId : ctx . currentDeploymentGroup . id
} , ( err , services = [ ] ) => {
2017-07-21 17:08:15 +03:00
if ( err && ! internals . isNotFound ( err ) ) {
2017-07-05 16:33:16 +03:00
return next ( err ) ;
2017-06-03 01:16:49 +03:00
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got ${ services . length } services with name ${ serviceName } from DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-06-03 01:16:49 +03:00
2017-07-05 16:33:16 +03:00
const provision = ctx . provisionRes [ serviceName ] ;
const action = Get ( provision , 'plan.action' , 'noop' ) . toUpperCase ( ) ;
const service = services . shift ( ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const { config } = Find ( ctx . config , [ 'name' , serviceName ] , {
config : { }
} ) ;
2017-07-05 16:33:16 +03:00
const payload = {
hash : provision . hash ,
deploymentGroupId : ctx . currentDeploymentGroup . id ,
name : serviceName ,
slug : ParamCase ( serviceName ) ,
2017-07-05 16:33:16 +03:00
status : ServiceStatusFromPlan [ action ] ,
config
2017-07-05 16:33:16 +03:00
} ;
return ! service ?
createProvisionService ( { payload } , next ) :
updateProvisionService ( { payload , serviceId : service . id } , next ) ;
2017-06-03 01:16:49 +03:00
} ) ;
2017-07-05 16:33:16 +03:00
} ;
2017-05-31 01:08:06 +03:00
2017-07-05 16:33:16 +03:00
// 8. handle `up` response
// 9. asynchronously iterate over services from provision response
const handleProvisionResponse = ( err , provisionRes ) => {
if ( err ) {
return handleFailedProvision ( err ) ;
}
if ( ctx . isHandled ) {
return ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got response from provision ${ Util . inspect ( provisionRes ) } ` ) ;
2017-07-05 16:33:16 +03:00
ctx . isHandled = true ;
ctx . provisionRes = provisionRes ;
VAsync . forEachParallel ( {
inputs : Object . keys ( ctx . provisionRes ) ,
func : handleProvisionService
} , handleNewServices ) ;
} ;
// 7. handle new version
// 8. call docker-compose to up dg
const handleNewVersion = ( err , newVersion ) => {
2017-05-31 01:08:06 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
// note: deployment group is updated when version is created
2017-05-31 01:08:06 +03:00
2017-07-05 16:33:16 +03:00
ctx . newVersion = newVersion ;
// cb with new version
// CALLBACK
cb ( null , ctx . newVersion ) ;
setImmediate ( ( ) => {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> requesting docker-compose provision for DeploymentGroup ${ ctx . currentDeploymentGroup . name } ` ) ;
2017-07-05 16:33:16 +03:00
this . _dockerCompose . provision ( {
projectName : ctx . currentDeploymentGroup . name ,
2017-07-05 16:33:16 +03:00
environment : clientManifest . environment ,
2017-07-21 17:08:15 +03:00
files : internals . fromKeyValueToDict ( clientManifest . files ) ,
2017-07-05 16:33:16 +03:00
manifest : ctx . newManifest . raw
} , handleProvisionResponse ) ;
} ) ;
} ;
// 6. handle curent scale based on machines in triton
// 7. create new version
const handleCurrentScale = ( err , currentScale ) => {
2017-05-31 01:08:06 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got current scale ${ Util . inspect ( currentScale ) } ` ) ;
2017-07-05 16:33:16 +03:00
ctx . currentScale = currentScale ;
2017-05-31 22:27:53 +03:00
2017-07-05 16:33:16 +03:00
this . createVersion ( {
manifest : ctx . newManifest ,
deploymentGroupId : ctx . currentDeploymentGroup . id ,
scale : currentScale ,
plan : [ ] ,
hasPlan : false
} , handleNewVersion ) ;
} ;
// 5. handle current version
// 6. get curent scale based on machines in triton
const handleCurrentVersion = ( err , currentVersion ) => {
if ( err ) {
return cb ( err ) ;
}
2017-05-31 22:27:53 +03:00
2017-07-05 16:33:16 +03:00
if ( ! currentVersion ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> detected first provision for DeploymentGroup ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-07-05 16:33:16 +03:00
} else {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating new Version based on old Version ${ currentVersion . id } ` ) ;
2017-07-05 16:33:16 +03:00
}
2017-05-31 22:27:53 +03:00
2017-07-05 16:33:16 +03:00
ctx . currentVersion = currentVersion ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
this . _getCurrentScale ( {
deploymentGroupName : ctx . currentDeploymentGroup . name ,
config : ctx . config ,
currentVersion
} , handleCurrentScale ) ;
} ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
// 4. handle new version
// 5. fetch current version
const handleNewManifest = ( err , newManifest ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-02 00:25:45 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching current version for ${ ctx . currentDeploymentGroup . id } ` ) ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
ctx . newManifest = newManifest ;
ctx . currentDeploymentGroup . version ( null , handleCurrentVersion ) ;
2017-06-22 20:09:13 +03:00
} ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
// 3. handle docker-compose config for the given manifest
// 4. create a new manifest
const handleConfig = ( err , config ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-02 00:25:45 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got docker-compose config ${ Util . inspect ( config ) } ` ) ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
ctx . config = config ;
2017-07-17 17:19:31 +03:00
this . createManifest ( Object . assign ( clientManifest , {
deploymentGroupId : ctx . currentDeploymentGroup . id
} ) , handleNewManifest ) ;
2017-06-22 20:09:13 +03:00
} ;
2017-07-05 16:33:16 +03:00
// 1. check if deployment group exists
// 2. update Deployment Group to set PROVISIONING status
// 3. get docker-compose config for the given manifest
const handleDeploymentGroup = ( err , currentDeploymentGroup ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! currentDeploymentGroup ) {
return cb ( new Error ( 'Deployment group not found for manifest' ) ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
if ( currentDeploymentGroup . status !== 'ACTIVE' ) {
console . error ( ` -> Can't provision when the status is " ${ currentDeploymentGroup . status } " ` ) ;
// return last version
return currentDeploymentGroup . version ( { } , cb ) ;
}
2017-06-02 00:25:45 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> DeploymentGroup found with id ${ currentDeploymentGroup . id } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const configPayload = Object . assign ( { } , clientManifest , {
deploymentGroupName : currentDeploymentGroup . name
} ) ;
2017-06-22 20:09:13 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> requesting docker-compose config for manifest ${ Util . inspect ( configPayload ) } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
ctx . currentDeploymentGroup = currentDeploymentGroup ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
this . updateDeploymentGroup ( {
id : ctx . currentDeploymentGroup . id ,
status : 'PROVISIONING'
} , ( err ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
this . getConfig ( configPayload , handleConfig ) ;
2017-06-02 00:25:45 +03:00
} ) ;
2017-06-22 20:09:13 +03:00
} ;
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
// 1. fetch current deployment group
this . getDeploymentGroup ( {
id : clientManifest . deploymentGroupId
} , handleDeploymentGroup ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
createManifest ( clientManifest , cb ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating new Manifest ${ Util . inspect ( clientManifest ) } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const newManifest = Transform . toManifest ( clientManifest ) ;
this . _db . manifests . insert ( newManifest , ( err , manifestId ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> new Manifest created with id ${ manifestId } ` ) ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
clientManifest . id = manifestId ;
cb ( null , Transform . fromManifest ( clientManifest ) ) ;
} ) ;
}
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
getManifest ( { id } , cb ) {
this . _db . manifests . single ( { id } , ( err , manifest ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-02 00:25:45 +03:00
2017-07-05 16:33:16 +03:00
cb ( null , Transform . fromManifest ( manifest || { } ) ) ;
} ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
getManifests ( { type , deploymentGroupId } , cb ) {
const query = type ? { type } : { deployment _group _id : deploymentGroupId } ;
this . _db . manifests . query ( query , ( err , manifests ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
manifests = manifests || [ ] ;
cb ( null , manifests . map ( Transform . fromManifest ) ) ;
} ) ;
2017-06-02 00:25:45 +03:00
}
2017-05-31 22:27:53 +03:00
createService ( clientService , cb ) {
2017-06-22 20:09:13 +03:00
const newService = Object . assign ( Transform . toService ( clientService ) , {
active : true
} ) ;
this . _db . services . insert ( newService , ( err , key ) => {
2017-05-31 22:27:53 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-02 00:25:45 +03:00
clientService . id = key ;
cb ( null , clientService ) ;
2017-05-31 22:27:53 +03:00
} ) ;
}
2017-06-22 20:09:13 +03:00
updateService ( clientService , cb ) {
2017-07-05 16:33:16 +03:00
const payload = Transform . toService ( clientService ) ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> got update Service request ${ Util . inspect ( payload ) } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . services . update ( [ payload ] , ( err ) => {
2017-06-22 20:09:13 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
this . getService ( { id : clientService . id } , cb ) ;
2017-06-22 20:09:13 +03:00
} ) ;
}
2017-05-31 22:27:53 +03:00
getService ( { id , hash } , cb ) {
const query = id ? { id } : { version _hash : hash } ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> fetching Service ${ Util . inspect ( query ) } ` ) ;
2017-07-05 16:33:16 +03:00
this . _db . services . query ( query , ( err , services ) => {
2017-05-31 22:27:53 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
if ( ! services || ! services . length ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> Service ${ Util . inspect ( query ) } not found ` ) ;
2017-07-21 17:08:15 +03:00
return cb ( Boom . notFound ( ) ) ;
2017-06-02 00:25:45 +03:00
}
2017-07-05 16:33:16 +03:00
const service = services . shift ( ) ;
2017-05-31 22:27:53 +03:00
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> Service ${ Util . inspect ( query ) } found ${ Util . inspect ( service ) } ` ) ;
2017-07-05 16:33:16 +03:00
2017-07-20 19:31:42 +03:00
const branches = ForceArray ( service . branches ) . map ( ( branch ) => {
2017-07-19 18:58:30 +03:00
return Object . assign ( { } , branch , {
instances : this . _instancesFilter ( branch . instances )
} ) ;
} ) ;
2017-07-05 16:33:16 +03:00
return cb ( null , Transform . fromService ( {
service ,
2017-07-19 18:58:30 +03:00
branches ,
2017-07-05 16:33:16 +03:00
instances : this . _instancesFilter ( service . instance _ids )
} ) ) ;
2017-05-31 22:27:53 +03:00
} ) ;
}
2017-06-09 21:13:22 +03:00
_getDeploymentGroupServices ( deploymentGroupSlug , cb ) {
this . getDeploymentGroup ( { slug : deploymentGroupSlug } , ( err , deploymentGroup ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! deploymentGroup ) {
2017-07-21 17:08:15 +03:00
return cb ( Boom . notFound ( ) ) ;
2017-06-09 21:13:22 +03:00
}
return this . getServices ( { deploymentGroupId : deploymentGroup . id } , cb ) ;
} ) ;
}
2017-06-08 00:35:45 +03:00
getServices ( options , cb ) {
2017-06-09 21:13:22 +03:00
if ( options . deploymentGroupSlug ) {
return this . _getDeploymentGroupServices ( options . deploymentGroupSlug , cb ) ;
}
2017-06-08 00:35:45 +03:00
const query = { } ;
if ( options . ids && options . ids . length ) {
query . id = this . _db . or ( options . ids ) ;
}
if ( options . name ) {
query . name = options . name ;
}
if ( options . slug ) {
query . slug = options . slug ;
}
if ( options . parentId ) {
query . parent _id = options . parentId ;
}
if ( options . deploymentGroupId ) {
query . deployment _group _id = options . deploymentGroupId ;
}
this . _db . services . query ( query , ( err , services ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-21 17:08:15 +03:00
if ( ( ( options . ids && options . ids . length ) || query . name || query . slug ) && ( ! services || ! services . length ) ) {
return cb ( Boom . notFound ( ) ) ;
}
2017-06-08 00:35:45 +03:00
if ( ! services || ! services . length ) {
return cb ( ) ;
}
return cb ( null , services . map ( ( service ) => {
2017-07-20 19:31:42 +03:00
const branches = ForceArray ( service . branches ) . map ( ( branch ) => {
2017-07-19 18:58:30 +03:00
return Object . assign ( { } , branch , {
instances : this . _instancesFilter ( branch . instances )
} ) ;
} ) ;
return Transform . fromService ( {
service ,
branches ,
instances : this . _instancesFilter ( service . instance _ids )
} ) ;
2017-06-08 00:35:45 +03:00
} ) ) ;
} ) ;
}
2017-05-31 22:27:53 +03:00
2017-07-05 16:33:16 +03:00
_instancesFilter ( instanceIds = [ ] ) {
return ( query , cb ) => {
2017-06-22 20:09:13 +03:00
query = query || { } ;
2017-07-05 16:33:16 +03:00
query . ids = instanceIds ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
if ( typeof cb === 'function' ) {
return instanceIds && instanceIds . length ?
this . getInstances ( query , cb ) :
cb ( null , [ ] ) ;
}
2017-06-08 23:50:12 +03:00
2017-07-05 16:33:16 +03:00
return new Promise ( ( resolve , reject ) => {
return instanceIds && instanceIds . length ?
this . getInstances ( query , internals . resolveCb ( resolve , reject ) ) :
resolve ( [ ] ) ;
2017-06-08 23:50:12 +03:00
} ) ;
} ;
}
2017-06-08 21:43:24 +03:00
stopServices ( { ids } , cb ) {
2017-07-05 16:33:16 +03:00
const revertStatus = ( err1 , cb ) => {
if ( err1 ) {
console . error ( err1 ) ;
2017-06-08 21:43:24 +03:00
}
VAsync . forEachParallel ( {
2017-07-05 16:33:16 +03:00
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , next ) ;
}
} , ( err2 ) => {
if ( err2 ) {
console . error ( err2 ) ;
}
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
if ( cb ) {
cb ( err1 || err2 ) ;
}
} ) ;
} ;
2017-06-29 17:38:30 +03:00
2017-07-05 16:33:16 +03:00
const handleUpdatedServices = ( {
currentServices
} ) => {
return ( err , result ) => {
2017-06-08 21:43:24 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return revertStatus ( err , cb ) ;
2017-06-08 21:43:24 +03:00
}
2017-07-05 16:33:16 +03:00
cb ( null , result . successes ) ;
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
setImmediate ( ( ) => {
const instanceIds = currentServices . reduce ( ( instanceIds , service ) => {
return instanceIds . concat ( service . instance _ids ) ;
} , [ ] ) ;
VAsync . forEachParallel ( {
inputs : instanceIds ,
func : ( instanceId , next ) => {
this . _db . instances . get ( instanceId , ( err , instance ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . stopMachine ( instance . machine _id , next ) ;
} ) ;
}
} , ( err ) => {
if ( err ) {
console . error ( err ) ;
}
} ) ;
} ) ;
} ;
} ;
const handleCurrentServices = ( err , currentServices ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
if ( ! currentServices || ! currentServices . length ) {
2017-06-08 23:50:12 +03:00
return cb ( ) ;
}
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
VAsync . forEachParallel ( {
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'STOPPING'
} , next ) ;
}
} , handleUpdatedServices ( {
currentServices
} ) ) ;
} ;
this . _db . services . get ( ids , handleCurrentServices ) ;
}
startServices ( { ids } , cb ) {
const revertStatus = ( err1 , cb ) => {
if ( err1 ) {
console . error ( err1 ) ;
}
2017-06-08 23:50:12 +03:00
VAsync . forEachParallel ( {
2017-07-05 16:33:16 +03:00
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , next ) ;
}
} , ( err2 ) => {
if ( err2 ) {
console . error ( err2 ) ;
}
2017-06-08 23:50:12 +03:00
2017-07-05 16:33:16 +03:00
if ( cb ) {
cb ( err1 || err2 ) ;
}
} ) ;
} ;
2017-06-29 17:38:30 +03:00
2017-07-05 16:33:16 +03:00
const handleUpdatedServices = ( {
currentServices
} ) => {
return ( err , result ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return revertStatus ( err , cb ) ;
2017-06-08 23:50:12 +03:00
}
2017-07-05 16:33:16 +03:00
cb ( null , result . successes ) ;
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
setImmediate ( ( ) => {
const instanceIds = currentServices . reduce ( ( instanceIds , service ) => {
return instanceIds . concat ( service . instance _ids ) ;
} , [ ] ) ;
VAsync . forEachParallel ( {
inputs : instanceIds ,
func : ( instanceId , next ) => {
this . _db . instances . get ( instanceId , ( err , instance ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . startMachine ( instance . machine _id , next ) ;
} ) ;
}
} , ( err ) => {
if ( err ) {
console . error ( err ) ;
}
} ) ;
} ) ;
} ;
} ;
const handleCurrentServices = ( err , currentServices ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
if ( ! currentServices || ! currentServices . length ) {
2017-06-08 23:50:12 +03:00
return cb ( ) ;
}
2017-07-05 16:33:16 +03:00
VAsync . forEachParallel ( {
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , next ) ;
}
} , handleUpdatedServices ( {
currentServices
} ) ) ;
} ;
this . _db . services . get ( ids , handleCurrentServices ) ;
}
restartServices ( { ids } , cb ) {
// 1. update all services statuses to RESTARTING
// 2. get all instances
// 3. restart all instances
// 4. revert service status
const revertStatus = ( err1 , cb ) => {
if ( err1 ) {
console . error ( err1 ) ;
}
2017-06-08 23:50:12 +03:00
VAsync . forEachParallel ( {
2017-07-05 16:33:16 +03:00
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , next ) ;
}
} , ( err2 ) => {
if ( err2 ) {
console . error ( err2 ) ;
}
2017-06-08 23:50:12 +03:00
2017-07-05 16:33:16 +03:00
if ( cb ) {
cb ( err1 || err2 ) ;
}
} ) ;
} ;
2017-06-29 17:38:30 +03:00
2017-07-05 16:33:16 +03:00
const handleUpdatedServices = ( {
currentServices
} ) => {
return ( err , result ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return revertStatus ( err , cb ) ;
2017-06-08 23:50:12 +03:00
}
2017-07-05 16:33:16 +03:00
cb ( null , result . successes ) ;
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
setImmediate ( ( ) => {
const instanceIds = currentServices . reduce ( ( instanceIds , service ) => {
return instanceIds . concat ( service . instance _ids ) ;
} , [ ] ) ;
VAsync . forEachParallel ( {
inputs : instanceIds ,
func : ( instanceId , next ) => {
this . _db . instances . get ( instanceId , ( err , instance ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . rebootMachine ( instance . machine _id , next ) ;
} ) ;
}
} , revertStatus ) ;
} ) ;
} ;
} ;
2017-06-22 20:09:13 +03:00
2017-07-05 16:33:16 +03:00
const handleCurrentServices = ( err , currentServices ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-06-08 21:43:24 +03:00
2017-07-05 16:33:16 +03:00
if ( ! currentServices || ! currentServices . length ) {
2017-06-08 23:50:12 +03:00
return cb ( ) ;
}
VAsync . forEachParallel ( {
2017-06-28 18:04:34 +03:00
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
2017-07-05 16:33:16 +03:00
status : 'RESTARTING'
2017-06-28 18:04:34 +03:00
} , next ) ;
}
2017-07-05 16:33:16 +03:00
} , handleUpdatedServices ( {
currentServices
} ) ) ;
} ;
this . _db . services . get ( ids , handleCurrentServices ) ;
}
deleteServices ( { ids } , cb ) {
const revertStatus = ( err1 , cb ) => {
if ( err1 ) {
console . error ( err1 ) ;
}
VAsync . forEachParallel ( {
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'ACTIVE'
} , next ) ;
}
} , ( err2 ) => {
if ( err2 ) {
console . error ( err2 ) ;
}
if ( cb ) {
cb ( err1 || err2 ) ;
}
} ) ;
} ;
const handleUpdatedServices = ( {
currentServices
} ) => {
return ( err , result ) => {
2017-06-08 23:50:12 +03:00
if ( err ) {
2017-07-05 16:33:16 +03:00
return revertStatus ( err , cb ) ;
2017-06-08 23:50:12 +03:00
}
2017-07-05 16:33:16 +03:00
cb ( null , result . successes ) ;
2017-06-28 18:04:34 +03:00
2017-07-05 16:33:16 +03:00
setImmediate ( ( ) => {
const instanceIds = currentServices . reduce ( ( instanceIds , service ) => {
return instanceIds . concat ( service . instance _ids ) ;
} , [ ] ) ;
VAsync . forEachParallel ( {
inputs : instanceIds ,
func : ( instanceId , next ) => {
this . _db . instances . get ( instanceId , ( err , instance ) => {
if ( err ) {
return next ( err ) ;
}
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . deleteMachine ( instance . machine _id , next ) ;
} ) ;
}
} , ( err ) => {
if ( err ) {
console . error ( err ) ;
}
} ) ;
} ) ;
} ;
} ;
2017-06-28 18:04:34 +03:00
2017-07-05 16:33:16 +03:00
const handleCurrentServices = ( err , currentServices ) => {
if ( err ) {
return cb ( err ) ;
}
2017-06-28 18:04:34 +03:00
2017-07-05 16:33:16 +03:00
if ( ! currentServices || ! currentServices . length ) {
return cb ( ) ;
}
2017-06-29 17:38:30 +03:00
2017-07-05 16:33:16 +03:00
VAsync . forEachParallel ( {
inputs : ids ,
func : ( serviceId , next ) => {
this . updateService ( {
id : serviceId ,
status : 'DELETING'
} , next ) ;
}
} , handleUpdatedServices ( {
currentServices
} ) ) ;
} ;
this . _db . services . get ( ids , handleCurrentServices ) ;
2017-06-08 21:43:24 +03:00
}
2017-05-31 22:27:53 +03:00
// instances
createInstance ( clientInstance , cb ) {
this . _db . instances . insert ( Transform . toInstance ( clientInstance ) , ( err , key ) => {
if ( err ) {
return cb ( err ) ;
}
clientInstance . id = key ;
cb ( null , clientInstance ) ;
} ) ;
}
getInstance ( { id } , cb ) {
this . _db . instances . single ( { id } , ( err , instance ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-21 17:08:15 +03:00
if ( ! instance ) {
return cb ( Boom . notFound ( ) ) ;
}
cb ( null , Transform . fromInstance ( instance ) ) ;
2017-05-31 22:27:53 +03:00
} ) ;
}
2017-06-09 23:30:48 +03:00
getInstances ( { ids , name , machineId , status } , cb ) {
const query = { } ;
if ( ids ) {
query . id = this . _db . or ( ids ) ;
}
if ( name ) {
query . name = name ;
}
if ( machineId ) {
query . machine _id = machineId ;
}
if ( status ) {
query . status = status ;
}
this . _db . instances . query ( query , ( err , instances ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-21 17:08:15 +03:00
if ( ( ( ids && ids . length ) || name || machineId ) && ( ! instances || ! instances . length ) ) {
return cb ( Boom . notFound ( ) ) ;
}
2017-06-09 23:30:48 +03:00
if ( ! instances || ! instances . length ) {
return cb ( null , [ ] ) ;
}
cb ( null , instances . map ( Transform . fromInstance ) ) ;
} ) ;
}
2017-07-05 16:33:16 +03:00
updateInstance ( clientInstance , cb ) {
const instance = Transform . toInstance ( clientInstance ) ;
2017-07-05 16:33:16 +03:00
2017-07-05 16:33:16 +03:00
this . _db . instances . update ( [ instance ] , ( err ) => {
2017-06-08 21:43:24 +03:00
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
this . getInstance ( { id : instance . id } , cb ) ;
2017-06-08 21:43:24 +03:00
} ) ;
}
2017-06-09 23:30:48 +03:00
stopInstances ( { ids } , cb ) {
this . _db . instances . get ( ids , ( err , instances ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! instances || ! instances . length ) {
return cb ( ) ;
}
VAsync . forEachParallel ( {
func : ( instance , next ) => {
2017-06-29 17:38:30 +03:00
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . stopMachine ( instance . machine _id , next ) ;
2017-06-09 23:30:48 +03:00
} ,
inputs : instances
} , ( err , results ) => {
if ( err ) {
return cb ( err ) ;
}
this . getInstances ( { ids } , cb ) ;
} ) ;
} ) ;
}
startInstances ( { ids } , cb ) {
this . _db . instances . get ( ids , ( err , instances ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! instances || ! instances . length ) {
return cb ( ) ;
}
VAsync . forEachParallel ( {
func : ( instance , next ) => {
2017-06-29 17:38:30 +03:00
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . startMachine ( instance . machine _id , ( err ) => {
2017-06-27 22:42:53 +03:00
if ( err ) {
return next ( err ) ;
}
2017-06-29 17:38:30 +03:00
const container = this . _docker . getContainer ( instance . machine _id . split ( /-/ ) [ 0 ] ) ;
2017-06-27 22:42:53 +03:00
// Update the IPAddress for the instance
container . inspect ( ( err , details ) => {
if ( err ) {
return next ( err ) ;
}
2017-06-29 17:38:30 +03:00
this . _db . instances . update ( instance . id , {
ips : [ details . NetworkSettings . IPAddress ]
} , next ) ;
2017-06-27 22:42:53 +03:00
} ) ;
} ) ;
2017-06-09 23:30:48 +03:00
} ,
inputs : instances
2017-06-27 22:42:53 +03:00
} , ( err ) => {
2017-06-09 23:30:48 +03:00
if ( err ) {
return cb ( err ) ;
}
this . getInstances ( { ids } , cb ) ;
} ) ;
} ) ;
}
restartInstances ( { ids } , cb ) {
this . _db . instances . get ( ids , ( err , instances ) => {
if ( err ) {
return cb ( err ) ;
}
if ( ! instances || ! instances . length ) {
return cb ( ) ;
}
VAsync . forEachParallel ( {
func : ( instance , next ) => {
2017-06-29 17:38:30 +03:00
if ( ! this . _triton ) {
return next ( ) ;
}
this . _triton . rebootMachine ( instance . machine _id , next ) ;
2017-06-09 23:30:48 +03:00
} ,
inputs : instances
} , ( err , results ) => {
if ( err ) {
return cb ( err ) ;
}
this . getInstances ( { ids } , cb ) ;
} ) ;
} ) ;
}
2017-05-31 22:27:53 +03:00
// packages
createPackage ( clientPackage , cb ) {
this . _db . packages . insert ( Transform . toPackage ( clientPackage ) , ( err , key ) => {
if ( err ) {
return cb ( err ) ;
}
clientPackage . id = key ;
cb ( null , clientPackage ) ;
} ) ;
}
getPackage ( { id } , cb ) {
this . _db . packages . single ( { id } , ( err , dbPackage ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-21 17:08:15 +03:00
if ( ! dbPackage ) {
return cb ( Boom . notFound ( ) ) ;
}
cb ( null , Transform . fromPackage ( dbPackage ) ) ;
2017-05-31 22:27:53 +03:00
} ) ;
}
getPackages ( { name , type } , cb ) {
const query = name ? { name } : { type } ;
this . _db . packages . query ( query , ( err , dbPackages ) => {
if ( err ) {
return cb ( err ) ;
}
cb ( null , dbPackages ? dbPackages . map ( Transform . fromPackage ) : [ ] ) ;
} ) ;
}
2017-06-23 00:59:25 +03:00
2017-07-15 06:38:27 +03:00
getConfig ( { deploymentGroupName = '' , type = '' , format = '' , environment = '' , files = [ ] , raw = '' } , cb ) {
2017-06-23 00:59:25 +03:00
if ( type . toUpperCase ( ) !== 'COMPOSE' ) {
return cb ( new Error ( '"COMPOSE" is the only `type` supported' ) ) ;
}
if ( format . toUpperCase ( ) !== 'YAML' ) {
return cb ( new Error ( '"YAML" is the only `format` supported' ) ) ;
}
let isFinished = false ;
this . _dockerCompose . config ( {
projectName : deploymentGroupName ,
2017-07-05 16:33:16 +03:00
environment ,
2017-07-21 17:08:15 +03:00
files : internals . fromKeyValueToDict ( files ) ,
2017-06-23 00:59:25 +03:00
manifest : raw
} , ( err , config = { } ) => {
if ( err ) {
return cb ( err ) ;
}
if ( isFinished ) {
return ;
}
isFinished = true ;
const { services } = config ;
if ( ! services || ! Object . keys ( services ) . length ) {
return cb ( null , [ ] ) ;
}
cb ( null , Object . keys ( services ) . reduce ( ( acc , serviceName ) => {
2017-07-05 16:33:16 +03:00
const environment = Get ( services , ` ${ serviceName } .environment ` , { } ) ;
const labels = Get ( services , ` ${ serviceName } .labels ` , { } ) ;
const ports = Get ( services , ` ${ serviceName } .ports ` , [ ] ) ;
const image = Get ( services , ` ${ serviceName } .image ` , '' ) ;
const toKeyValue = ( v ) => {
return Object . keys ( v ) . map ( ( key ) => {
return {
id : Uuid ( ) ,
name : key ,
value : v [ key ]
} ;
} ) ;
} ;
2017-06-23 00:59:25 +03:00
return acc . concat ( [ {
id : Uuid ( ) ,
hash : Uuid ( ) ,
name : serviceName ,
slug : ParamCase ( serviceName ) ,
instances : [ ] ,
2017-07-05 16:33:16 +03:00
config : {
id : Uuid ( ) ,
environment : toKeyValue ( environment ) ,
image : image ,
labels : toKeyValue ( labels ) ,
ports : ports
}
2017-06-23 00:59:25 +03:00
} ] ) ;
} , [ ] ) ) ;
} ) ;
}
2017-06-26 17:29:12 +03:00
getImportableDeploymentGroups ( args , cb ) {
2017-07-11 19:59:25 +03:00
if ( ! this . _machines ) {
2017-06-26 17:29:12 +03:00
return cb ( null , [ ] ) ;
}
2017-07-11 19:59:25 +03:00
const machines = this . _machines . getContainers ( ) ;
2017-06-26 17:29:12 +03:00
if ( ! Array . isArray ( machines ) ) {
return cb ( null , [ ] ) ;
}
2017-06-27 18:14:00 +03:00
this . getDeploymentGroups ( { } , ( err , dgs ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-05 16:33:16 +03:00
const names = dgs . map ( ( { name } ) => {
return name ;
} ) ;
2017-06-27 18:14:00 +03:00
return cb (
null ,
UniqBy (
machines
2017-07-05 16:33:16 +03:00
. filter ( ( { tags = { } } ) => {
return names . indexOf ( tags [ DEPLOYMENT _GROUP ] ) < 0 ;
} )
. filter ( ( { state } ) => {
return NON _IMPORTABLE _STATES . indexOf ( state . toUpperCase ( ) ) < 0 ;
} )
. filter ( ( { tags = { } } ) => {
return [ DEPLOYMENT _GROUP , SERVICE , HASH ] . every ( ( name ) => {
return tags [ name ] ;
} ) ;
} )
2017-06-27 18:14:00 +03:00
. map ( ( { tags = { } } ) => {
return ( {
id : Uuid ( ) ,
name : tags [ DEPLOYMENT _GROUP ] ,
slug : ParamCase ( tags [ DEPLOYMENT _GROUP ] )
} ) ;
} ) ,
'slug'
)
) ;
} ) ;
2017-06-26 17:29:12 +03:00
}
importDeploymentGroup ( { deploymentGroupSlug } , cb ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> import requested for ${ deploymentGroupSlug } ` ) ;
2017-06-26 17:29:12 +03:00
2017-07-11 19:59:25 +03:00
if ( ! this . _machines ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> watcher not yet defined' ) ;
2017-06-26 17:29:12 +03:00
return cb ( null , null ) ;
}
2017-07-11 19:59:25 +03:00
const machines = this . _machines . getContainers ( ) ;
2017-06-26 17:29:12 +03:00
if ( ! Array . isArray ( machines ) ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , '-> no machines found' ) ;
2017-06-26 17:29:12 +03:00
return cb ( null , null ) ;
}
const containers = machines
. filter (
2017-07-05 16:33:16 +03:00
( { tags = { } } ) => {
return tags [ DEPLOYMENT _GROUP ] && ParamCase ( tags [ DEPLOYMENT _GROUP ] ) === deploymentGroupSlug ;
}
2017-06-26 17:29:12 +03:00
)
. filter (
2017-07-05 16:33:16 +03:00
( { state } ) => {
return NON _IMPORTABLE _STATES . indexOf ( state . toUpperCase ( ) ) < 0 ;
}
2017-06-26 17:29:12 +03:00
) ;
if ( ! containers . length ) {
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> no containers found for ${ deploymentGroupSlug } ` ) ;
2017-06-26 17:29:12 +03:00
return cb ( null , null ) ;
}
const { tags = [ ] } = containers [ 0 ] ;
const services = containers . reduce ( ( acc , { tags = [ ] , id = '' , state = '' , name = '' } ) => {
const hash = tags [ HASH ] ;
const slug = ParamCase ( tags [ SERVICE ] ) ;
const attr = ` ${ hash } - ${ slug } ` ;
const instance = {
name : name ,
machineId : id ,
status : state . toUpperCase ( )
} ;
if ( acc [ attr ] ) {
acc [ attr ] . instances . push ( instance ) ;
return acc ;
}
return Object . assign ( acc , {
[ attr ] : {
hash ,
name : tags [ SERVICE ] ,
2017-07-19 19:06:45 +03:00
status : 'ACTIVE' ,
2017-06-26 17:29:12 +03:00
slug ,
instances : [ instance ]
}
} ) ;
} , { } ) ;
const createService = ( deploymentGroupId ) => {
return ( serviceId , next ) => {
const service = services [ serviceId ] ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating Service ${ Util . inspect ( service ) } ` ) ;
2017-06-26 17:29:12 +03:00
VAsync . forEachParallel ( {
inputs : service . instances ,
2017-07-05 16:33:16 +03:00
func : ( instance , next ) => {
2017-07-17 17:19:31 +03:00
return this . createInstance ( Object . assign ( instance , {
deploymentGroupId
} ) , next ) ;
2017-07-05 16:33:16 +03:00
}
2017-06-26 17:29:12 +03:00
} , ( err , results ) => {
if ( err ) {
return cb ( err ) ;
}
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> created Instances ${ Util . inspect ( results . successes ) } ` ) ;
2017-06-26 17:29:12 +03:00
this . createService ( Object . assign ( service , {
instances : results . successes ,
deploymentGroupId
} ) , next ) ;
} ) ;
} ;
} ;
const deploymentGroup = {
name : tags [ DEPLOYMENT _GROUP ] ,
2017-06-27 18:13:15 +03:00
slug : ParamCase ( tags [ DEPLOYMENT _GROUP ] ) ,
2017-07-19 19:06:45 +03:00
status : 'ACTIVE' ,
2017-06-27 18:13:15 +03:00
imported : true
2017-06-26 17:29:12 +03:00
} ;
2017-07-31 23:10:04 +03:00
this . _server . log ( [ 'debug' ] , ` -> creating DeploymentGroup ${ Util . inspect ( deploymentGroup ) } ` ) ;
2017-06-26 17:29:12 +03:00
this . createDeploymentGroup ( deploymentGroup , ( err , dg ) => {
if ( err ) {
return cb ( err ) ;
}
VAsync . forEachParallel ( {
inputs : Object . keys ( services ) ,
func : createService ( dg . id )
2017-07-05 16:33:16 +03:00
} , ( err ) => {
return cb ( err , dg ) ;
} ) ;
2017-06-26 17:29:12 +03:00
} ) ;
}
2017-07-05 16:33:16 +03:00
}
module . exports = Data ;
module . exports . UNKNOWN _INSTANCE _ID = UNKNOWN _INSTANCE _ID ;
module . exports . NEW _INSTANCE _ID = NEW _INSTANCE _ID ;