2020-07-05 23:21:03 +00:00
import angular from 'angular' ;
2021-02-26 15:50:33 +00:00
import _ from 'lodash-es' ;
2020-07-05 23:21:03 +00:00
import filesizeParser from 'filesize-parser' ;
2021-07-14 09:15:21 +00:00
import { KubernetesResourceQuotaDefaults } from 'Kubernetes/models/resource-quota/models' ;
2020-07-05 23:21:03 +00:00
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper' ;
2021-07-28 02:26:03 +00:00
import { KubernetesResourceReservation } from 'Kubernetes/models/resource-reservation/models' ;
2020-07-05 23:21:03 +00:00
import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper' ;
2021-04-27 17:51:13 +00:00
import {
KubernetesResourcePoolFormValues ,
KubernetesResourcePoolIngressClassAnnotationFormValue ,
KubernetesResourcePoolIngressClassHostFormValue ,
} from 'Kubernetes/models/resource-pool/formValues' ;
2020-08-20 00:51:14 +00:00
import { KubernetesIngressConverter } from 'Kubernetes/ingress/converter' ;
2021-02-26 15:50:33 +00:00
import { KubernetesFormValidationReferences } from 'Kubernetes/models/application/formValues' ;
2020-08-20 00:51:14 +00:00
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper' ;
2020-08-20 09:24:12 +00:00
import { KubernetesIngressClassTypes } from 'Kubernetes/ingress/constants' ;
2021-02-26 15:50:33 +00:00
import KubernetesResourceQuotaConverter from 'Kubernetes/converters/resourceQuota' ;
2021-08-26 14:00:59 +00:00
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper' ;
2021-10-06 06:24:26 +00:00
import { K8S _RESOURCE _POOL _LB _QUOTA , K8S _RESOURCE _POOL _STORAGE _QUOTA } from '@/portainer/feature-flags/feature-ids' ;
2020-07-05 23:21:03 +00:00
class KubernetesResourcePoolController {
2020-08-20 00:51:14 +00:00
/* #region CONSTRUCTOR */
2020-07-05 23:21:03 +00:00
/* @ngInject */
constructor (
$async ,
$state ,
Authentication ,
Notifications ,
LocalStorage ,
2021-07-14 09:15:21 +00:00
EndpointService ,
2020-08-12 23:30:23 +00:00
ModalService ,
2020-07-05 23:21:03 +00:00
KubernetesNodeService ,
2021-07-28 02:26:03 +00:00
KubernetesMetricsService ,
2020-07-05 23:21:03 +00:00
KubernetesResourceQuotaService ,
KubernetesResourcePoolService ,
KubernetesEventService ,
KubernetesPodService ,
2020-08-07 00:03:00 +00:00
KubernetesApplicationService ,
2021-02-26 15:50:33 +00:00
KubernetesIngressService ,
KubernetesVolumeService
2020-07-05 23:21:03 +00:00
) {
2021-07-14 09:15:21 +00:00
Object . assign ( this , {
$async ,
$state ,
Authentication ,
Notifications ,
LocalStorage ,
EndpointService ,
ModalService ,
KubernetesNodeService ,
2021-07-28 02:26:03 +00:00
KubernetesMetricsService ,
2021-07-14 09:15:21 +00:00
KubernetesResourceQuotaService ,
KubernetesResourcePoolService ,
KubernetesEventService ,
KubernetesPodService ,
KubernetesApplicationService ,
KubernetesIngressService ,
KubernetesVolumeService ,
} ) ;
2020-07-05 23:21:03 +00:00
2020-08-20 09:24:12 +00:00
this . IngressClassTypes = KubernetesIngressClassTypes ;
2021-02-26 15:50:33 +00:00
this . ResourceQuotaDefaults = KubernetesResourceQuotaDefaults ;
2020-08-20 09:24:12 +00:00
2021-10-06 06:24:26 +00:00
this . LBQuotaFeatureId = K8S _RESOURCE _POOL _LB _QUOTA ;
this . StorageQuotaFeatureId = K8S _RESOURCE _POOL _STORAGE _QUOTA ;
2020-07-05 23:21:03 +00:00
this . updateResourcePoolAsync = this . updateResourcePoolAsync . bind ( this ) ;
this . getEvents = this . getEvents . bind ( this ) ;
}
2020-08-20 00:51:14 +00:00
/* #endregion */
2021-04-27 17:51:13 +00:00
/* #region ANNOTATIONS MANAGEMENT */
addAnnotation ( ingressClass ) {
ingressClass . Annotations . push ( new KubernetesResourcePoolIngressClassAnnotationFormValue ( ) ) ;
}
removeAnnotation ( ingressClass , index ) {
ingressClass . Annotations . splice ( index , 1 ) ;
this . onChangeIngressHostname ( ) ;
}
/* #endregion */
/* #region INGRESS MANAGEMENT */
2020-08-20 00:51:14 +00:00
onChangeIngressHostname ( ) {
const state = this . state . duplicates . ingressHosts ;
const otherIngresses = _ . without ( this . allIngresses , ... this . ingresses ) ;
2021-04-27 17:51:13 +00:00
const allHosts = _ . flatMap ( otherIngresses , 'Hosts' ) ;
const hosts = _ . flatMap ( this . formValues . IngressClasses , 'Hosts' ) ;
const hostsWithoutRemoved = _ . filter ( hosts , { NeedsDeletion : false } ) ;
const hostnames = _ . map ( hostsWithoutRemoved , 'Host' ) ;
const formDuplicates = KubernetesFormValidationHelper . getDuplicates ( hostnames ) ;
_ . forEach ( hostnames , ( host , idx ) => {
if ( host !== undefined && _ . includes ( allHosts , host ) ) {
formDuplicates [ idx ] = host ;
2020-08-20 00:51:14 +00:00
}
} ) ;
2021-04-27 17:51:13 +00:00
const duplicatedHostnames = Object . values ( formDuplicates ) ;
state . hasRefs = false ;
_ . forEach ( this . formValues . IngressClasses , ( ic ) => {
_ . forEach ( ic . Hosts , ( hostFV ) => {
if ( _ . includes ( duplicatedHostnames , hostFV . Host ) && hostFV . NeedsDeletion === false ) {
hostFV . Duplicate = true ;
state . hasRefs = true ;
} else {
hostFV . Duplicate = false ;
}
} ) ;
} ) ;
2020-08-20 00:51:14 +00:00
}
2021-04-27 17:51:13 +00:00
addHostname ( ingressClass ) {
ingressClass . Hosts . push ( new KubernetesResourcePoolIngressClassHostFormValue ( ) ) ;
2020-08-20 00:51:14 +00:00
}
2021-04-27 17:51:13 +00:00
removeHostname ( ingressClass , index ) {
if ( ! ingressClass . Hosts [ index ] . IsNew ) {
ingressClass . Hosts [ index ] . NeedsDeletion = true ;
} else {
ingressClass . Hosts . splice ( index , 1 ) ;
}
this . onChangeIngressHostname ( ) ;
2020-08-20 00:51:14 +00:00
}
2021-04-27 17:51:13 +00:00
restoreHostname ( host ) {
if ( ! host . IsNew ) {
host . NeedsDeletion = false ;
}
}
/* #endregion*/
2020-07-05 23:21:03 +00:00
selectTab ( index ) {
this . LocalStorage . storeActiveTab ( 'resourcePool' , index ) ;
}
2020-08-20 00:51:14 +00:00
isUpdateButtonDisabled ( ) {
2021-02-26 15:50:33 +00:00
return this . state . actionInProgress || ( this . formValues . HasQuota && ! this . isQuotaValid ( ) ) || this . state . duplicates . ingressHosts . hasRefs ;
2020-08-20 00:51:14 +00:00
}
2020-07-05 23:21:03 +00:00
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 ( ) {
2021-02-26 15:50:33 +00:00
if ( this . formValues . CpuLimit < KubernetesResourceQuotaDefaults . CpuLimit ) {
this . formValues . CpuLimit = KubernetesResourceQuotaDefaults . CpuLimit ;
2020-07-05 23:21:03 +00:00
}
2021-02-26 15:50:33 +00:00
if ( this . formValues . MemoryLimit < KubernetesResourceReservationHelper . megaBytesValue ( KubernetesResourceQuotaDefaults . MemoryLimit ) ) {
this . formValues . MemoryLimit = KubernetesResourceReservationHelper . megaBytesValue ( KubernetesResourceQuotaDefaults . MemoryLimit ) ;
2020-07-05 23:21:03 +00:00
}
}
showEditor ( ) {
this . state . showEditorTab = true ;
this . selectTab ( 2 ) ;
}
2020-08-12 23:30:23 +00:00
hasResourceQuotaBeenReduced ( ) {
if ( this . formValues . HasQuota && this . oldQuota ) {
2020-08-07 22:46:11 +00:00
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 ;
}
2021-04-27 08:12:34 +00:00
/* #region UPDATE NAMESPACE */
2021-08-26 14:00:59 +00:00
async updateResourcePoolAsync ( oldFormValues , newFormValues ) {
2020-07-05 23:21:03 +00:00
this . state . actionInProgress = true ;
try {
this . checkDefaults ( ) ;
2021-08-26 14:00:59 +00:00
await this . KubernetesResourcePoolService . patch ( oldFormValues , newFormValues ) ;
2021-04-27 08:12:34 +00:00
this . Notifications . success ( 'Namespace successfully updated' , this . pool . Namespace . Name ) ;
2021-09-24 08:21:50 +00:00
this . $state . reload ( this . $state . current ) ;
2020-07-05 23:21:03 +00:00
} catch ( err ) {
2021-04-27 08:12:34 +00:00
this . Notifications . error ( 'Failure' , err , 'Unable to create namespace' ) ;
2020-07-05 23:21:03 +00:00
} finally {
this . state . actionInProgress = false ;
}
}
updateResourcePool ( ) {
2021-09-06 05:25:02 +00:00
const ingressesToDelete = _ . filter ( this . formValues . IngressClasses , { WasSelected : true , Selected : false } ) ;
const registriesToDelete = _ . filter ( this . registries , { WasChecked : true , Checked : false } ) ;
2020-08-12 23:30:23 +00:00
const warnings = {
quota : this . hasResourceQuotaBeenReduced ( ) ,
2021-09-06 05:25:02 +00:00
ingress : ingressesToDelete . length !== 0 ,
registries : registriesToDelete . length !== 0 ,
2020-08-12 23:30:23 +00:00
} ;
2021-09-06 05:25:02 +00:00
if ( warnings . quota || warnings . ingress || warnings . registries ) {
2020-08-12 23:30:23 +00:00
const messages = {
quota :
2021-04-27 08:12:34 +00:00
'Reducing the quota assigned to an "in-use" namespace may have unintended consequences, including preventing running applications from functioning correctly and potentially even blocking them from running at all.' ,
2020-08-12 23:30:23 +00:00
ingress : 'Deactivating ingresses may cause applications to be unaccessible. All ingress configurations from affected applications will be removed.' ,
2021-09-06 05:25:02 +00:00
registries :
'Some registries you removed might be used by one or more applications inside this environment. Removing the registries access could lead to a service interruption for these applications.' ,
2020-08-12 23:30:23 +00:00
} ;
2021-09-06 05:25:02 +00:00
const displayedMessage = ` ${ warnings . quota ? messages . quota + '<br/><br/>' : '' }
$ { warnings . ingress ? messages . ingress + '<br/><br/>' : '' }
$ { warnings . registries ? messages . registries + '<br/><br/>' : '' }
Do you wish to continue ? ` ;
2020-08-12 23:30:23 +00:00
this . ModalService . confirmUpdate ( displayedMessage , ( confirmed ) => {
if ( confirmed ) {
2021-08-26 14:00:59 +00:00
return this . $async ( this . updateResourcePoolAsync , this . savedFormValues , this . formValues ) ;
2020-08-07 22:46:11 +00:00
}
2020-08-12 23:30:23 +00:00
} ) ;
2020-08-07 22:46:11 +00:00
} else {
2021-08-26 14:00:59 +00:00
return this . $async ( this . updateResourcePoolAsync , this . savedFormValues , this . formValues ) ;
2020-08-07 22:46:11 +00:00
}
2020-07-05 23:21:03 +00:00
}
2021-08-26 14:00:59 +00:00
async confirmMarkUnmarkAsSystem ( ) {
const message = this . isSystem
? 'Unmarking this namespace as system will allow non administrator users to manage it and the resources in contains depending on the access control settings. Are you sure?'
: 'Marking this namespace as a system namespace will prevent non administrator users from managing it and the resources it contains. Are you sure?' ;
return new Promise ( ( resolve ) => {
this . ModalService . confirmUpdate ( message , resolve ) ;
} ) ;
}
markUnmarkAsSystem ( ) {
return this . $async ( async ( ) => {
try {
const namespaceName = this . $state . params . id ;
this . state . actionInProgress = true ;
const confirmed = await this . confirmMarkUnmarkAsSystem ( ) ;
if ( ! confirmed ) {
return ;
}
await this . KubernetesResourcePoolService . toggleSystem ( this . endpoint . Id , namespaceName , ! this . isSystem ) ;
this . Notifications . success ( 'Namespace successfully updated' , namespaceName ) ;
2021-09-24 08:21:50 +00:00
this . $state . reload ( this . $state . current ) ;
2021-08-26 14:00:59 +00:00
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to create namespace' ) ;
} finally {
this . state . actionInProgress = false ;
}
} ) ;
}
2021-02-26 15:50:33 +00:00
/* #endregion */
2020-07-05 23:21:03 +00:00
hasEventWarnings ( ) {
return this . state . eventWarningCount ;
}
2020-08-20 00:51:14 +00:00
/* #region GET EVENTS */
2020-07-05 23:21:03 +00:00
getEvents ( ) {
2021-02-26 15:50:33 +00:00
return this . $async ( async ( ) => {
try {
this . state . eventsLoading = true ;
this . events = await this . KubernetesEventService . get ( this . pool . Namespace . Name ) ;
this . state . eventWarningCount = KubernetesEventHelper . warningCount ( this . events ) ;
} catch ( err ) {
2021-04-27 08:12:34 +00:00
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve namespace related events' ) ;
2021-02-26 15:50:33 +00:00
} finally {
this . state . eventsLoading = false ;
}
} ) ;
2020-07-05 23:21:03 +00:00
}
2020-08-20 00:51:14 +00:00
/* #endregion */
2020-07-05 23:21:03 +00:00
2020-08-20 00:51:14 +00:00
/* #region GET APPLICATIONS */
2020-07-05 23:21:03 +00:00
getApplications ( ) {
2021-02-26 15:50:33 +00:00
return this . $async ( async ( ) => {
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 ;
} ) ;
2021-07-28 02:26:03 +00:00
2021-08-13 04:46:18 +00:00
if ( this . state . useServerMetrics ) {
await this . getResourceUsage ( this . pool . Namespace . Name ) ;
}
2021-02-26 15:50:33 +00:00
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve applications.' ) ;
} finally {
this . state . applicationsLoading = false ;
}
} ) ;
2020-07-05 23:21:03 +00:00
}
2020-08-20 00:51:14 +00:00
/* #endregion */
2020-07-05 23:21:03 +00:00
2020-08-20 00:51:14 +00:00
/* #region GET INGRESSES */
2020-08-12 23:30:23 +00:00
getIngresses ( ) {
2021-02-26 15:50:33 +00:00
return this . $async ( async ( ) => {
this . state . ingressesLoading = true ;
try {
const namespace = this . pool . Namespace . Name ;
2021-08-31 21:43:11 +00:00
this . allIngresses = await this . KubernetesIngressService . get ( this . state . hasWriteAuthorization ? '' : namespace ) ;
2021-02-26 15:50:33 +00:00
this . ingresses = _ . filter ( this . allIngresses , { Namespace : namespace } ) ;
_ . forEach ( this . ingresses , ( ing ) => {
ing . Namespace = namespace ;
_ . forEach ( ing . Paths , ( path ) => {
const application = _ . find ( this . applications , { ServiceName : path . ServiceName } ) ;
path . ApplicationName = application && application . Name ? application . Name : '-' ;
} ) ;
} ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve ingresses.' ) ;
} finally {
this . state . ingressesLoading = false ;
}
} ) ;
2020-08-12 23:30:23 +00:00
}
2020-08-20 00:51:14 +00:00
/* #endregion */
2020-08-12 23:30:23 +00:00
2021-07-14 09:15:21 +00:00
/* #region GET REGISTRIES */
getRegistries ( ) {
return this . $async ( async ( ) => {
try {
const namespace = this . $state . params . id ;
if ( this . isAdmin ) {
this . registries = await this . EndpointService . registries ( this . endpoint . Id ) ;
this . registries . forEach ( ( reg ) => {
if ( reg . RegistryAccesses && reg . RegistryAccesses [ this . endpoint . Id ] && reg . RegistryAccesses [ this . endpoint . Id ] . Namespaces . includes ( namespace ) ) {
reg . Checked = true ;
2021-09-06 05:25:02 +00:00
reg . WasChecked = true ;
2021-07-14 09:15:21 +00:00
this . formValues . Registries . push ( reg ) ;
}
} ) ;
2021-09-03 13:47:37 +00:00
this . selectedRegistries = this . formValues . Registries . map ( ( r ) => r . Name ) . join ( ', ' ) ;
2021-07-14 09:15:21 +00:00
return ;
}
const registries = await this . EndpointService . registries ( this . endpoint . Id , namespace ) ;
this . selectedRegistries = registries . map ( ( r ) => r . Name ) . join ( ', ' ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve registries' ) ;
}
} ) ;
}
/* #endregion */
2021-07-28 02:26:03 +00:00
async getResourceUsage ( namespace ) {
try {
const namespaceMetrics = await this . KubernetesMetricsService . getPods ( namespace ) ;
// extract resource usage of all containers within each pod of the namespace
const containerResourceUsageList = namespaceMetrics . items . flatMap ( ( i ) => i . containers . map ( ( c ) => c . usage ) ) ;
const namespaceResourceUsage = containerResourceUsageList . reduce ( ( total , u ) => {
total . CPU += KubernetesResourceReservationHelper . parseCPU ( u . cpu ) ;
total . Memory += KubernetesResourceReservationHelper . megaBytesValue ( u . memory ) ;
return total ;
} , new KubernetesResourceReservation ( ) ) ;
this . state . resourceUsage = namespaceResourceUsage ;
} catch ( err ) {
2021-10-11 21:37:07 +00:00
this . Notifications . error ( 'Failure' , err , 'Unable to retrieve namespace resource usage' ) ;
2021-07-28 02:26:03 +00:00
}
}
2021-07-14 09:15:21 +00:00
/* #region ON INIT */
$onInit ( ) {
return this . $async ( async ( ) => {
try {
this . isAdmin = this . Authentication . isAdmin ( ) ;
this . state = {
actionInProgress : false ,
sliderMaxMemory : 0 ,
sliderMaxCpu : 0 ,
cpuUsage : 0 ,
memoryUsage : 0 ,
2021-07-28 02:26:03 +00:00
resourceReservation : { CPU : 0 , Memory : 0 } ,
2021-07-14 09:15:21 +00:00
activeTab : 0 ,
currentName : this . $state . $current . name ,
showEditorTab : false ,
eventsLoading : true ,
applicationsLoading : true ,
ingressesLoading : true ,
viewReady : false ,
eventWarningCount : 0 ,
2021-07-28 02:26:03 +00:00
canUseIngress : this . endpoint . Kubernetes . Configuration . IngressClasses . length ,
useServerMetrics : this . endpoint . Kubernetes . Configuration . UseServerMetrics ,
2021-07-14 09:15:21 +00:00
duplicates : {
ingressHosts : new KubernetesFormValidationReferences ( ) ,
} ,
} ;
this . state . activeTab = this . LocalStorage . getActiveTab ( 'resourcePool' ) ;
const name = this . $state . params . id ;
const [ nodes , pools ] = await Promise . all ( [ this . KubernetesNodeService . get ( ) , this . KubernetesResourcePoolService . get ( ) ] ) ;
this . pool = _ . find ( pools , { Namespace : { Name : name } } ) ;
this . formValues = new KubernetesResourcePoolFormValues ( KubernetesResourceQuotaDefaults ) ;
this . formValues . Name = this . pool . Namespace . Name ;
this . formValues . EndpointId = this . endpoint . Id ;
2021-08-26 14:00:59 +00:00
this . formValues . IsSystem = this . pool . Namespace . IsSystem ;
2021-07-14 09:15:21 +00:00
_ . forEach ( nodes , ( item ) => {
this . state . sliderMaxMemory += filesizeParser ( item . Memory ) ;
this . state . sliderMaxCpu += item . CPU ;
} ) ;
this . state . sliderMaxMemory = KubernetesResourceReservationHelper . megaBytesValue ( this . state . sliderMaxMemory ) ;
2020-07-05 23:21:03 +00:00
2021-07-14 09:15:21 +00:00
const quota = this . pool . Quota ;
if ( quota ) {
this . oldQuota = angular . copy ( quota ) ;
this . formValues = KubernetesResourceQuotaConverter . quotaToResourcePoolFormValues ( quota ) ;
this . formValues . EndpointId = this . endpoint . Id ;
2020-07-05 23:21:03 +00:00
2021-07-28 02:26:03 +00:00
this . state . resourceReservation . CPU = quota . CpuLimitUsed ;
this . state . resourceReservation . Memory = KubernetesResourceReservationHelper . megaBytesValue ( quota . MemoryLimitUsed ) ;
2021-07-14 09:15:21 +00:00
}
2021-08-26 14:00:59 +00:00
this . isSystem = KubernetesNamespaceHelper . isSystemNamespace ( this . pool . Namespace . Name ) ;
this . isDefaultNamespace = KubernetesNamespaceHelper . isDefaultNamespace ( this . pool . Namespace . Name ) ;
this . isEditable = ! this . isSystem && ! this . isDefaultNamespace ;
2020-07-05 23:21:03 +00:00
2021-07-14 09:15:21 +00:00
await this . getEvents ( ) ;
await this . getApplications ( ) ;
if ( this . state . canUseIngress ) {
await this . getIngresses ( ) ;
2021-07-28 02:26:03 +00:00
const ingressClasses = this . endpoint . Kubernetes . Configuration . IngressClasses ;
2021-07-14 09:15:21 +00:00
this . formValues . IngressClasses = KubernetesIngressConverter . ingressClassesToFormValues ( ingressClasses , this . ingresses ) ;
_ . forEach ( this . formValues . IngressClasses , ( ic ) => {
if ( ic . Hosts . length === 0 ) {
ic . Hosts . push ( new KubernetesResourcePoolIngressClassHostFormValue ( ) ) ;
}
} ) ;
}
2020-07-05 23:21:03 +00:00
2021-07-14 09:15:21 +00:00
await this . getRegistries ( ) ;
2020-08-07 00:03:00 +00:00
2021-07-14 09:15:21 +00:00
this . savedFormValues = angular . copy ( this . formValues ) ;
} catch ( err ) {
this . Notifications . error ( 'Failure' , err , 'Unable to load view data' ) ;
} finally {
this . state . viewReady = true ;
2020-08-12 23:30:23 +00:00
}
2021-07-14 09:15:21 +00:00
} ) ;
2020-07-05 23:21:03 +00:00
}
2020-08-20 00:51:14 +00:00
/* #endregion */
2020-07-05 23:21:03 +00:00
$onDestroy ( ) {
if ( this . state . currentName !== this . $state . $current . name ) {
this . LocalStorage . storeActiveTab ( 'resourcePool' , 0 ) ;
}
}
}
export default KubernetesResourcePoolController ;
angular . module ( 'portainer.kubernetes' ) . controller ( 'KubernetesResourcePoolController' , KubernetesResourcePoolController ) ;