209
209
:aria-label =" t('app_api', 'Compute device')"
210
210
:options =" computeDevices" />
211
211
</div >
212
+ <div class =" external-label" :aria-label =" t('app_api', 'Memory limit')" >
213
+ <label for =" memory-limit" >
214
+ {{ t('app_api', 'Memory limit (in MB)') }}
215
+ <InfoTooltip :text =" t('app_api', 'Maximum memory that the ExApp container can use in megabytes')" />
216
+ </label >
217
+ <NcInputField
218
+ id =" memory-limit"
219
+ ref =" memory-limit"
220
+ class =" ex-input-field"
221
+ :value.sync =" memoryLimit"
222
+ :placeholder =" t('app_api', 'Memory limit in MB')"
223
+ :aria-label =" t('app_api', 'Memory limit in MB')"
224
+ :error =" isMemoryLimitValid === false"
225
+ :helper-text =" isMemoryLimitValid === false ? t('app_api', 'Must be a positive integer') : ''" />
226
+ </div >
227
+ <div class =" external-label" :aria-label =" t('app_api', 'CPU limit')" >
228
+ <label for =" cpu-limit" >
229
+ {{ t('app_api', 'CPU limit') }}
230
+ <InfoTooltip :text =" t('app_api', 'Maximum CPU cores that the ExApp container can use (e.g. 0.5 for half a core, 2 for two cores)')" />
231
+ </label >
232
+ <NcInputField
233
+ id =" cpu-limit"
234
+ ref =" cpu-limit"
235
+ class =" ex-input-field"
236
+ :value.sync =" cpuLimit"
237
+ :placeholder =" t('app_api', 'CPU limit as decimal value')"
238
+ :aria-label =" t('app_api', 'CPU limit')"
239
+ :error =" isCpuLimitValid === false"
240
+ :helper-text =" isCpuLimitValid === false ? t('app_api', 'Must be a positive number') : ''" />
241
+ </div >
212
242
<template v-if =" additionalOptions .length > 0 " >
213
243
<div class =" row" style =" flex-direction : column ;" >
214
244
<div
@@ -413,12 +443,26 @@ export default {
413
443
data .defaultDaemon = this .isDefaultDaemon
414
444
data .additionalOptions = Object .entries (this .daemon .deploy_config .additional_options ?? {}).map (([key , value ]) => ({ key, value }))
415
445
data .deployConfigSettingsOpened = true
446
+ if (data .deployConfig .resourceLimits ) {
447
+ if (data .deployConfig .resourceLimits .memory ) {
448
+ data .deployConfig .resourceLimits .memoryMB = data .deployConfig .resourceLimits .memory / (1024 * 1024 )
449
+ delete data .deployConfig .resourceLimits .memory
450
+ }
451
+ if (data .deployConfig .resourceLimits .nanoCPUs ) {
452
+ data .deployConfig .resourceLimits .cpus = data .deployConfig .resourceLimits .nanoCPUs / 1000000000
453
+ delete data .deployConfig .resourceLimits .nanoCPUs
454
+ }
455
+ }
416
456
}
417
457
if (! data .deployConfig .harp ) {
418
458
data .deployConfig .harp = null
419
459
data .deployConfigSettingsOpened = false
420
460
}
421
461
462
+ if (! data .deployConfig .resourceLimits ) {
463
+ data .deployConfig .resourceLimits = { memoryMB: null , cpus: null }
464
+ }
465
+
422
466
return data
423
467
},
424
468
computed: {
@@ -437,6 +481,32 @@ export default {
437
481
daemonProtocol () {
438
482
return this .httpsEnabled ? ' https' : ' http'
439
483
},
484
+ memoryLimit: {
485
+ get () {
486
+ return this .deployConfig .resourceLimits .memoryMB || ' '
487
+ },
488
+ set (value ) {
489
+ this .deployConfig .resourceLimits .memoryMB = value === ' ' ? null : value
490
+ },
491
+ },
492
+ cpuLimit: {
493
+ get () {
494
+ return this .deployConfig .resourceLimits .cpus || ' '
495
+ },
496
+ set (value ) {
497
+ this .deployConfig .resourceLimits .cpus = value === ' ' ? null : value
498
+ },
499
+ },
500
+ isMemoryLimitValid () {
501
+ if (this .memoryLimit === ' ' || this .memoryLimit === null ) return true
502
+ const str = String (this .memoryLimit ).trim ()
503
+ return / ^ [1-9 ] \d * $ / .test (str)
504
+ },
505
+ isCpuLimitValid () {
506
+ if (this .cpuLimit === ' ' || this .cpuLimit === null ) return true
507
+ const str = String (this .cpuLimit ).trim ()
508
+ return / ^ \d * \. ? \d + $ / .test (str)
509
+ },
440
510
isDaemonNameInvalid () {
441
511
return this .daemons .some (daemon => daemon .name === this .name && daemon .name !== this .daemon ? .name )
442
512
},
@@ -463,7 +533,7 @@ export default {
463
533
return t (' app_api' , ' The docker network that the deployed ex-apps would use.' )
464
534
},
465
535
cannotRegister () {
466
- return this .isDaemonNameInvalid === true || this .isHaProxyPasswordValid === false || (this .isHarp && ! this .deployConfig .net )
536
+ return this .isDaemonNameInvalid === true || this .isHaProxyPasswordValid === false || (this .isHarp && ! this .deployConfig .net ) || this . isMemoryLimitValid === false || this . isCpuLimitValid === false
467
537
},
468
538
isAdditionalOptionValid () {
469
539
return this .additionalOption .key .trim () !== ' ' && this .additionalOption .value .trim () !== ' '
@@ -619,6 +689,16 @@ export default {
619
689
registries: this .deployConfig .registries || null ,
620
690
},
621
691
}
692
+
693
+ const resourceLimits = {}
694
+ if (this .deployConfig .resourceLimits .memoryMB && this .isMemoryLimitValid ) {
695
+ resourceLimits .memory = Number (this .deployConfig .resourceLimits .memoryMB ) * 1024 * 1024
696
+ }
697
+ if (this .deployConfig .resourceLimits .cpus && this .isCpuLimitValid ) {
698
+ resourceLimits .nanoCPUs = Number (this .deployConfig .resourceLimits .cpus ) * 1000000000
699
+ }
700
+ params .deploy_config .resourceLimits = resourceLimits
701
+
622
702
if (this .additionalOptions .length > 0 ) {
623
703
params .deploy_config .additional_options = this .additionalOptions .reduce ((acc , option ) => {
624
704
acc[option .key ] = option .value
0 commit comments