2020-07-05 23:21:03 +00:00
import angular from 'angular' ;
import _ from 'lodash-es' ;
import filesizeParser from 'filesize-parser' ;
2020-08-07 22:46:11 +00:00
import { KubernetesResourceQuota , KubernetesResourceQuotaDefaults } from 'Kubernetes/models/resource-quota/models' ;
2020-07-05 23:21:03 +00:00
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper' ;
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper' ;
class KubernetesResourcePoolController {
/* @ngInject */
constructor (
$async ,
$state ,
Authentication ,
Notifications ,
LocalStorage ,
KubernetesNodeService ,
KubernetesResourceQuotaService ,
KubernetesResourcePoolService ,
KubernetesEventService ,
KubernetesPodService ,
2020-08-07 00:03:00 +00:00
KubernetesApplicationService ,
2020-08-07 22:46:11 +00:00
KubernetesNamespaceHelper ,
ModalService
2020-07-05 23:21:03 +00:00
) {
this . $async = $async ;
this . $state = $state ;
this . Notifications = Notifications ;
this . Authentication = Authentication ;
this . LocalStorage = LocalStorage ;
this . KubernetesNodeService = KubernetesNodeService ;
this . KubernetesResourceQuotaService = KubernetesResourceQuotaService ;
this . KubernetesResourcePoolService = KubernetesResourcePoolService ;
this . KubernetesEventService = KubernetesEventService ;
this . KubernetesPodService = KubernetesPodService ;
this . KubernetesApplicationService = KubernetesApplicationService ;
2020-08-07 00:03:00 +00:00
this . KubernetesNamespaceHelper = KubernetesNamespaceHelper ;
2020-08-07 22:46:11 +00:00
this . ModalService = ModalService ;
2020-07-05 23:21:03 +00:00
this . onInit = this . onInit . bind ( this ) ;
this . createResourceQuotaAsync = this . createResourceQuotaAsync . bind ( this ) ;
this . updateResourcePoolAsync = this . updateResourcePoolAsync . bind ( this ) ;
this . getEvents = this . getEvents . bind ( this ) ;
this . getEventsAsync = this . getEventsAsync . bind ( this ) ;
this . getApplicationsAsync = this . getApplicationsAsync . bind ( this ) ;
}
selectTab ( index ) {
this . LocalStorage . storeActiveTab ( 'resourcePool' , index ) ;
}
isQuotaValid ( ) {
if (
this . state . sliderMaxCpu < this . formValues . CpuLimit ||
this . state . sliderMaxMemory < this . formValues . MemoryLimit ||
( this . formValues . CpuLimit === 0 && this . formValues . MemoryLimit === 0 )
) {
return false ;
}
return true ;
}
checkDefaults ( ) {
if ( this . formValues . CpuLimit < this . defaults . CpuLimit ) {
this . formValues . CpuLimit = this . defaults . CpuLimit ;
}
if ( this . formValues . MemoryLimit < KubernetesResourceReservationHelper . megaBytesValue ( this . defaults . MemoryLimit ) ) {
this . formValues . MemoryLimit = KubernetesResourceReservationHelper . megaBytesValue ( this . defaults . MemoryLimit ) ;
}
}
showEditor ( ) {
this . state . showEditorTab = true ;
this . selectTab ( 2 ) ;
}
async createResourceQuotaAsync ( namespace , owner , cpuLimit , memoryLimit ) {
const quota = new KubernetesResourceQuota ( namespace ) ;
quota . CpuLimit = cpuLimit ;
quota . MemoryLimit = memoryLimit ;
quota . ResourcePoolName = namespace ;
quota . ResourcePoolOwner = owner ;
await this . KubernetesResourceQuotaService . create ( quota ) ;
}
2020-08-07 22:46:11 +00:00
hasResourceQuotaBeenReduce ( ) {
if ( this . formValues . hasQuota ) {
const cpuLimit = this . formValues . CpuLimit ;
const memoryLimit = KubernetesResourceReservationHelper . bytesValue ( this . formValues . MemoryLimit ) ;
if ( cpuLimit < this . oldQuota . CpuLimit || memoryLimit < this . oldQuota . MemoryLimit ) {
return true ;
}
}
return false ;
}
2020-07-05 23:21:03 +00:00
async updateResourcePoolAsync ( ) {
this . state . actionInProgress = true ;
try {
this . checkDefaults ( ) ;
const namespace = this . pool . Namespace . Name ;
const cpuLimit = this . formValues . CpuLimit ;
const memoryLimit = KubernetesResourceReservationHelper . bytesValue ( this . formValues . MemoryLimit ) ;
const owner = this . pool . Namespace . ResourcePoolOwner ;
const quota = this . pool . Quota ;
if ( this . formValues . hasQuota ) {
if ( quota ) {
quota . CpuLimit = cpuLimit ;
quota . MemoryLimit = memoryLimit ;
await this . KubernetesResourceQuotaService . update ( quota ) ;
} else {
await this . createResourceQuotaAsync ( namespace , owner , cpuLimit , memoryLimit ) ;
}
} else if ( quota ) {
await this . KubernetesResourceQuotaService . delete ( quota ) ;
}
this . Notifications . success ( 'Resource pool successfully updated' , this . pool . Namespace . Name ) ;
this . $state . reload ( ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to create resource pool' ) ;
} finally {
this . state . actionInProgress = false ;
}
}
updateResourcePool ( ) {
2020-08-07 22:46:11 +00:00
if ( this . hasResourceQuotaBeenReduce ( ) ) {
this . ModalService . confirmUpdate (
'Reducing the quota assigned to an "in-use" resource pool may have unintended consequences, including preventing running applications from functioning correctly and potentially even blocking them from running at all.' ,
( confirmed ) => {
if ( confirmed ) {
return this . $async ( this . updateResourcePoolAsync ) ;
}
}
) ;
} else {
return this . $async ( this . updateResourcePoolAsync ) ;
}
2020-07-05 23:21:03 +00:00
}
hasEventWarnings ( ) {
return this . state . eventWarningCount ;
}
async getEventsAsync ( ) {
try {
this . state . eventsLoading = true ;
this . events = await this . KubernetesEventService . get ( this . pool . Namespace . Name ) ;
this . state . eventWarningCount = KubernetesEventHelper . warningCount ( this . events ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve resource pool related events' ) ;
} finally {
this . state . eventsLoading = false ;
}
}
getEvents ( ) {
return this . $async ( this . getEventsAsync ) ;
}
async getApplicationsAsync ( ) {
try {
this . state . applicationsLoading = true ;
this . applications = await this . KubernetesApplicationService . get ( this . pool . Namespace . Name ) ;
this . applications = _ . map ( this . applications , ( app ) => {
const resourceReservation = KubernetesResourceReservationHelper . computeResourceReservation ( app . Pods ) ;
app . CPU = resourceReservation . CPU ;
app . Memory = resourceReservation . Memory ;
return app ;
} ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve applications.' ) ;
} finally {
this . state . applicationsLoading = false ;
}
}
getApplications ( ) {
return this . $async ( this . getApplicationsAsync ) ;
}
async onInit ( ) {
try {
this . isAdmin = this . Authentication . isAdmin ( ) ;
this . defaults = KubernetesResourceQuotaDefaults ;
this . formValues = {
MemoryLimit : this . defaults . MemoryLimit ,
CpuLimit : this . defaults . CpuLimit ,
hasQuota : false ,
} ;
this . state = {
actionInProgress : false ,
sliderMaxMemory : 0 ,
sliderMaxCpu : 0 ,
cpuUsage : 0 ,
cpuUsed : 0 ,
memoryUsage : 0 ,
memoryUsed : 0 ,
activeTab : 0 ,
currentName : this . $state . $current . name ,
showEditorTab : false ,
eventsLoading : true ,
applicationsLoading : true ,
viewReady : false ,
eventWarningCount : 0 ,
} ;
this . state . activeTab = this . LocalStorage . getActiveTab ( 'resourcePool' ) ;
const name = this . $transition$ . params ( ) . id ;
const [ nodes , pool ] = await Promise . all ( [ this . KubernetesNodeService . get ( ) , this . KubernetesResourcePoolService . get ( name ) ] ) ;
this . pool = pool ;
_ . forEach ( nodes , ( item ) => {
this . state . sliderMaxMemory += filesizeParser ( item . Memory ) ;
this . state . sliderMaxCpu += item . CPU ;
} ) ;
this . state . sliderMaxMemory = KubernetesResourceReservationHelper . megaBytesValue ( this . state . sliderMaxMemory ) ;
const quota = pool . Quota ;
if ( quota ) {
2020-08-07 22:46:11 +00:00
this . oldQuota = angular . copy ( quota ) ;
2020-07-05 23:21:03 +00:00
this . formValues . hasQuota = true ;
this . formValues . CpuLimit = quota . CpuLimit ;
this . formValues . MemoryLimit = KubernetesResourceReservationHelper . megaBytesValue ( quota . MemoryLimit ) ;
this . state . cpuUsed = quota . CpuLimitUsed ;
this . state . memoryUsed = KubernetesResourceReservationHelper . megaBytesValue ( quota . MemoryLimitUsed ) ;
}
2020-08-07 00:03:00 +00:00
this . isEditable = ! this . KubernetesNamespaceHelper . isSystemNamespace ( this . pool . Namespace . Name ) ;
if ( this . pool . Namespace . Name === 'default' ) {
this . isEditable = false ;
}
2020-07-05 23:21:03 +00:00
await this . getEvents ( ) ;
await this . getApplications ( ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to load view data' ) ;
} finally {
this . state . viewReady = true ;
}
}
$onInit ( ) {
return this . $async ( this . onInit ) ;
}
$onDestroy ( ) {
if ( this . state . currentName !== this . $state . $current . name ) {
this . LocalStorage . storeActiveTab ( 'resourcePool' , 0 ) ;
}
}
}
export default KubernetesResourcePoolController ;
angular . module ( 'portainer.kubernetes' ) . controller ( 'KubernetesResourcePoolController' , KubernetesResourcePoolController ) ;