@@ -56,6 +56,11 @@ import { GroupWithRole } from 'src/group/entities/group'
5656import { isEqual } from 'lodash'
5757import { InstanceService } from 'src/instance/instance.service'
5858import { QuotaService } from 'src/user/quota.service'
59+ import { DedicatedDatabaseService } from 'src/database/dedicated-database/dedicated-database.service'
60+ import {
61+ DedicatedDatabasePhase ,
62+ DedicatedDatabaseState ,
63+ } from 'src/database/entities/dedicated-database'
5964
6065@ApiTags ( 'Application' )
6166@Controller ( 'applications' )
@@ -73,6 +78,7 @@ export class ApplicationController {
7378 private readonly resource : ResourceService ,
7479 private readonly runtimeDomain : RuntimeDomainService ,
7580 private readonly quotaServiceTsService : QuotaService ,
81+ private readonly dedicateDatabase : DedicatedDatabaseService ,
7682 ) { }
7783
7884 /**
@@ -253,6 +259,7 @@ export class ApplicationController {
253259 if ( dto . state === ApplicationState . Deleted ) {
254260 throw new ForbiddenException ( 'cannot update state to deleted' )
255261 }
262+ const ddb = await this . dedicateDatabase . findOne ( appid )
256263 const userid = app . createdBy
257264
258265 // check account balance
@@ -265,8 +272,10 @@ export class ApplicationController {
265272 // check: only running application can restart
266273 if (
267274 dto . state === ApplicationState . Restarting &&
268- app . state !== ApplicationState . Running &&
269- app . phase !== ApplicationPhase . Started
275+ ! (
276+ app . state === ApplicationState . Running &&
277+ app . phase === ApplicationPhase . Started
278+ )
270279 ) {
271280 return ResponseUtil . error (
272281 'The application is not running, can not restart it' ,
@@ -276,8 +285,8 @@ export class ApplicationController {
276285 // check: only running application can stop
277286 if (
278287 dto . state === ApplicationState . Stopped &&
279- app . state !== ApplicationState . Running &&
280- app . phase !== ApplicationPhase . Started
288+ ( app . state !== ApplicationState . Running ||
289+ app . phase !== ApplicationPhase . Started )
281290 ) {
282291 return ResponseUtil . error (
283292 'The application is not running, can not stop it' ,
@@ -287,8 +296,8 @@ export class ApplicationController {
287296 // check: only stopped application can start
288297 if (
289298 dto . state === ApplicationState . Running &&
290- app . state !== ApplicationState . Stopped &&
291- app . phase !== ApplicationPhase . Stopped
299+ ( app . state !== ApplicationState . Stopped ||
300+ app . phase !== ApplicationPhase . Stopped )
292301 ) {
293302 return ResponseUtil . error (
294303 'The application is not stopped, can not start it' ,
@@ -304,6 +313,20 @@ export class ApplicationController {
304313 return ResponseUtil . error ( 'no permission' )
305314 }
306315
316+ if ( ddb ) {
317+ if ( dto . state === ApplicationState . Restarting && dto ?. onlyRuntimeFlag ) {
318+ const doc = await this . application . updateState ( appid , dto . state )
319+ return ResponseUtil . ok ( doc )
320+ }
321+
322+ const doc = await this . application . updateState ( appid , dto . state )
323+ await this . dedicateDatabase . updateState (
324+ appid ,
325+ dto . state as unknown as DedicatedDatabaseState ,
326+ )
327+ return ResponseUtil . ok ( doc )
328+ }
329+
307330 const doc = await this . application . updateState ( appid , dto . state )
308331 return ResponseUtil . ok ( doc )
309332 }
@@ -322,6 +345,13 @@ export class ApplicationController {
322345 @InjectApplication ( ) app : ApplicationWithRelations ,
323346 @InjectUser ( ) user : User ,
324347 ) {
348+ // only running application can update bundle
349+ if ( app . phase !== ApplicationPhase . Started ) {
350+ return ResponseUtil . error (
351+ 'The application is not running, can not update bundle' ,
352+ )
353+ }
354+
325355 const error = dto . autoscaling . validate ( )
326356 if ( error ) {
327357 return ResponseUtil . error ( error )
@@ -354,11 +384,44 @@ export class ApplicationController {
354384 return ResponseUtil . error ( 'cannot change database type' )
355385 }
356386
357- const checkSpec = await this . checkResourceSpecification ( dto , regionId )
387+ const checkSpec = await this . checkResourceSpecification ( dto , regionId , app )
358388 if ( ! checkSpec ) {
359389 return ResponseUtil . error ( 'invalid resource specification' )
360390 }
361391
392+ // Check if user is trying to change dedicated database resources
393+ const isTryingToChangeDedicatedDatabase =
394+ ( dto . dedicatedDatabase ?. cpu !== undefined &&
395+ dto . dedicatedDatabase ?. cpu !==
396+ origin . resource . dedicatedDatabase ?. limitCPU ) ||
397+ ( dto . dedicatedDatabase ?. memory !== undefined &&
398+ dto . dedicatedDatabase ?. memory !==
399+ origin . resource . dedicatedDatabase ?. limitMemory ) ||
400+ ( dto . dedicatedDatabase ?. replicas !== undefined &&
401+ dto . dedicatedDatabase ?. replicas !==
402+ origin . resource . dedicatedDatabase ?. replicas ) ||
403+ ( dto . dedicatedDatabase ?. capacity !== undefined &&
404+ dto . dedicatedDatabase ?. capacity !==
405+ origin . resource . dedicatedDatabase ?. capacity )
406+
407+ if ( isTryingToChangeDedicatedDatabase ) {
408+ const ddb = await this . dedicateDatabase . findOne ( appid )
409+ // Database must be running to change database resources
410+ if ( ! ddb ) {
411+ return ResponseUtil . error (
412+ 'DedicatedDatabase not found, cannot change DedicatedDatabase database resources' ,
413+ )
414+ }
415+ if (
416+ ddb . state !== DedicatedDatabaseState . Running ||
417+ ddb . phase !== DedicatedDatabasePhase . Started
418+ ) {
419+ return ResponseUtil . error (
420+ 'DedicatedDatabase is not in running state, cannot change DedicatedDatabase database resources' ,
421+ )
422+ }
423+ }
424+
362425 if (
363426 dto . dedicatedDatabase ?. capacity &&
364427 origin . resource . dedicatedDatabase ?. capacity &&
@@ -393,12 +456,15 @@ export class ApplicationController {
393456 const doc = await this . application . updateBundle ( appid , dto , isTrialTier )
394457
395458 // restart running application if cpu or memory changed
396- const isRunning = app . phase === ApplicationPhase . Started
397459 const isCpuChanged = origin . resource . limitCPU !== doc . resource . limitCPU
398460 const isMemoryChanged =
399461 origin . resource . limitMemory !== doc . resource . limitMemory
400462 const isAutoscalingCanceled =
401463 ! doc . autoscaling . enable && origin . autoscaling . enable
464+
465+ const isRuntimeChanged =
466+ isCpuChanged || isMemoryChanged || isAutoscalingCanceled
467+
402468 const isDedicatedDatabaseChanged =
403469 ! ! origin . resource . dedicatedDatabase &&
404470 ( ! isEqual (
@@ -423,13 +489,16 @@ export class ApplicationController {
423489 await this . instance . reapplyHorizontalPodAutoscaler ( app , hpa )
424490 }
425491
426- if (
427- isRunning &&
428- ( isCpuChanged ||
429- isMemoryChanged ||
430- isAutoscalingCanceled ||
431- isDedicatedDatabaseChanged )
432- ) {
492+ if ( isDedicatedDatabaseChanged ) {
493+ await this . application . updateState ( appid , ApplicationState . Restarting )
494+ await this . dedicateDatabase . updateState (
495+ appid ,
496+ DedicatedDatabaseState . Restarting ,
497+ )
498+ return ResponseUtil . ok ( doc )
499+ }
500+
501+ if ( isRuntimeChanged ) {
433502 await this . application . updateState ( appid , ApplicationState . Restarting )
434503 }
435504
@@ -540,8 +609,80 @@ export class ApplicationController {
540609 private async checkResourceSpecification (
541610 dto : UpdateApplicationBundleDto ,
542611 regionId : ObjectId ,
612+ app ?: ApplicationWithRelations ,
543613 ) {
544614 const resourceOptions = await this . resource . findAllByRegionId ( regionId )
615+
616+ if ( app ) {
617+ const checkSpec = resourceOptions . every ( ( option ) => {
618+ switch ( option . type ) {
619+ case 'cpu' :
620+ return (
621+ option . specs . some ( ( spec ) => spec . value === dto . cpu ) ||
622+ app . bundle . resource . limitCPU === dto . cpu
623+ )
624+ case 'memory' :
625+ return (
626+ option . specs . some ( ( spec ) => spec . value === dto . memory ) ||
627+ app . bundle . resource . limitMemory === dto . memory
628+ )
629+ case 'databaseCapacity' :
630+ if ( ! dto . databaseCapacity ) return true
631+ return (
632+ option . specs . some (
633+ ( spec ) => spec . value === dto . databaseCapacity ,
634+ ) || app . bundle . resource . databaseCapacity === dto . databaseCapacity
635+ )
636+ case 'storageCapacity' :
637+ if ( ! dto . storageCapacity ) return true
638+ return (
639+ option . specs . some ( ( spec ) => spec . value === dto . storageCapacity ) ||
640+ app . bundle . resource . storageCapacity === dto . storageCapacity
641+ )
642+ // dedicated database
643+ case 'dedicatedDatabaseCPU' :
644+ return (
645+ ! dto . dedicatedDatabase ?. cpu ||
646+ option . specs . some (
647+ ( spec ) => spec . value === dto . dedicatedDatabase . cpu ,
648+ ) ||
649+ app . bundle . resource . dedicatedDatabase ?. limitCPU ===
650+ dto . dedicatedDatabase . cpu
651+ )
652+ case 'dedicatedDatabaseMemory' :
653+ return (
654+ ! dto . dedicatedDatabase ?. memory ||
655+ option . specs . some (
656+ ( spec ) => spec . value === dto . dedicatedDatabase . memory ,
657+ ) ||
658+ app . bundle . resource . dedicatedDatabase ?. limitMemory ===
659+ dto . dedicatedDatabase . memory
660+ )
661+ case 'dedicatedDatabaseCapacity' :
662+ return (
663+ ! dto . dedicatedDatabase ?. capacity ||
664+ option . specs . some (
665+ ( spec ) => spec . value === dto . dedicatedDatabase . capacity ,
666+ ) ||
667+ app . bundle . resource . dedicatedDatabase ?. capacity ===
668+ dto . dedicatedDatabase . capacity
669+ )
670+ case 'dedicatedDatabaseReplicas' :
671+ return (
672+ ! dto . dedicatedDatabase ?. replicas ||
673+ option . specs . some (
674+ ( spec ) => spec . value === dto . dedicatedDatabase . replicas ,
675+ ) ||
676+ app . bundle . resource . dedicatedDatabase ?. replicas ===
677+ dto . dedicatedDatabase . replicas
678+ )
679+ default :
680+ return true
681+ }
682+ } )
683+ return checkSpec
684+ }
685+
545686 const checkSpec = resourceOptions . every ( ( option ) => {
546687 switch ( option . type ) {
547688 case 'cpu' :
0 commit comments