1
- import { PercentageCircle } from '@kinvolk/headlamp-plugin/lib/CommonComponents ' ;
2
- import { Box , IconButton , Popover , Stack , Tooltip , Typography , useTheme } from '@mui/material' ;
1
+ import { ApiError } from '@kinvolk/headlamp-plugin/lib/lib/k8s/apiProxy ' ;
2
+ import { Box , IconButton , Popover , Stack , Tooltip , useTheme } from '@mui/material' ;
3
3
import React from 'react' ;
4
+ import { BorderedSection } from '../../components/BorderedSection' ;
5
+ import { LoadingWrapper } from '../../components/LoadingWrapper' ;
6
+ import { DEFAULT_CLUSTER } from '../../constants/clusters' ;
4
7
import { ResourceQuotaKubeObject } from '../../k8s/groups/default/ResourceQuota' ;
5
8
import { RESOURCE_QUOTA_LABEL_TENANT } from '../../k8s/groups/default/ResourceQuota/labels' ;
9
+ import { ResourceQuotaKubeObjectInterface } from '../../k8s/groups/default/ResourceQuota/types' ;
10
+ import { StageKubeObject } from '../../k8s/groups/EDP/Stage' ;
11
+ import { StageKubeObjectInterface } from '../../k8s/groups/EDP/Stage/types' ;
6
12
import { getDefaultNamespace } from '../../utils/getDefaultNamespace' ;
7
13
import { CircleProgress } from './components/CircleProgress' ;
14
+ import { RQItem } from './components/RQItem' ;
15
+ import { ParsedQuotas , QuotaDetails } from './types' ;
8
16
import { getColorByLoadPercentage , parseResourceQuota } from './utils' ;
9
17
10
18
export const ResourceQuotas = ( ) => {
11
- const [ items ] = ResourceQuotaKubeObject . useList ( {
12
- labelSelector : `${ RESOURCE_QUOTA_LABEL_TENANT } =${ getDefaultNamespace ( ) } ` ,
19
+ const defaultNamespace = getDefaultNamespace ( ) ;
20
+
21
+ const [ globalRQs , setGlobalRQs ] = React . useState < {
22
+ quotas : ParsedQuotas ;
23
+ highestUsedQuota : QuotaDetails | null ;
24
+ } > ( null ) ;
25
+ const [ globalRQsError , setGlobalRQsError ] = React . useState < Error | ApiError > ( null ) ;
26
+
27
+ const handleSetGlobalRQs = React . useCallback ( ( items : ResourceQuotaKubeObjectInterface [ ] ) => {
28
+ if ( items ?. length === 0 ) {
29
+ setGlobalRQs ( {
30
+ quotas : { } ,
31
+ highestUsedQuota : null ,
32
+ } ) ;
33
+ return ;
34
+ }
35
+
36
+ const useAnnotations = Object . keys ( items [ 0 ] ?. metadata ?. annotations || { } ) . some ( ( key ) =>
37
+ key . includes ( 'quota.capsule.clastix.io' )
38
+ ) ;
39
+
40
+ setGlobalRQs ( parseResourceQuota ( items , useAnnotations ) ) ;
41
+ } , [ ] ) ;
42
+
43
+ ResourceQuotaKubeObject . useApiList ( handleSetGlobalRQs , setGlobalRQsError , {
44
+ namespace : defaultNamespace ,
45
+ labelSelector : `${ RESOURCE_QUOTA_LABEL_TENANT } =${ defaultNamespace } ` ,
13
46
} ) ;
14
47
15
- const { quotas, highestUsedQuota } = React . useMemo ( ( ) => {
16
- if ( items === null || items ?. length === 0 ) {
17
- return { quotas : null , highestUsedQuota : null } ;
48
+ const [ firstInClusterStage , setFirstInClusterStage ] =
49
+ React . useState < StageKubeObjectInterface > ( null ) ;
50
+ const [ stagesError , setStagesError ] = React . useState < Error | ApiError > ( null ) ;
51
+
52
+ const stageIsLoading = firstInClusterStage === null && ! stagesError ;
53
+
54
+ const handleGetFirstInClusterStage = React . useCallback ( ( stages : StageKubeObjectInterface [ ] ) => {
55
+ const firstFind = stages . find ( ( stage ) => stage . spec . clusterName === DEFAULT_CLUSTER ) ;
56
+ setFirstInClusterStage ( firstFind ) ;
57
+ } , [ ] ) ;
58
+
59
+ StageKubeObject . useApiList ( handleGetFirstInClusterStage , setStagesError , {
60
+ namespace : defaultNamespace ,
61
+ } ) ;
62
+
63
+ const [ stageRQs , setStageRQs ] = React . useState < {
64
+ quotas : ParsedQuotas ;
65
+ highestUsedQuota : QuotaDetails | null ;
66
+ } > ( null ) ;
67
+ const [ stageRQsError , setStageRQsError ] = React . useState < Error | ApiError > ( null ) ;
68
+
69
+ const handleSetStageRQs = React . useCallback ( ( items : ResourceQuotaKubeObjectInterface [ ] ) => {
70
+ if ( items ?. length === 0 ) {
71
+ setStageRQs ( {
72
+ quotas : { } ,
73
+ highestUsedQuota : null ,
74
+ } ) ;
75
+ return ;
18
76
}
19
77
20
78
const useAnnotations = Object . keys ( items [ 0 ] ?. metadata ?. annotations || { } ) . some ( ( key ) =>
21
79
key . includes ( 'quota.capsule.clastix.io' )
22
80
) ;
23
81
24
- return parseResourceQuota ( items , useAnnotations ) ;
25
- } , [ items ] ) ;
82
+ setStageRQs ( parseResourceQuota ( items , useAnnotations ) ) ;
83
+ } , [ ] ) ;
84
+
85
+ React . useEffect ( ( ) => {
86
+ if ( stageIsLoading ) {
87
+ return ;
88
+ }
89
+
90
+ const cancelStream = ResourceQuotaKubeObject . streamList ( {
91
+ namespace : firstInClusterStage ?. spec . namespace ,
92
+ tenantNamespace : defaultNamespace ,
93
+ dataHandler : handleSetStageRQs ,
94
+ errorHandler : setStageRQsError ,
95
+ } ) ;
96
+
97
+ return ( ) => cancelStream ( ) ;
98
+ } , [ defaultNamespace , firstInClusterStage ?. spec . namespace , handleSetStageRQs , stageIsLoading ] ) ;
99
+
100
+ const highestUsedQuota = React . useMemo ( ( ) => {
101
+ if ( globalRQs === null || stageRQs === null ) {
102
+ return null ;
103
+ }
104
+
105
+ if ( globalRQs . highestUsedQuota === null && stageRQs . highestUsedQuota === null ) {
106
+ return null ;
107
+ }
108
+
109
+ if ( globalRQs . highestUsedQuota === null ) {
110
+ return stageRQs . highestUsedQuota ;
111
+ }
112
+
113
+ if ( stageRQs . highestUsedQuota === null ) {
114
+ return globalRQs . highestUsedQuota ;
115
+ }
116
+
117
+ return globalRQs . highestUsedQuota . usedPercentage > stageRQs . highestUsedQuota . usedPercentage
118
+ ? globalRQs . highestUsedQuota
119
+ : stageRQs . highestUsedQuota ;
120
+ } , [ globalRQs , stageRQs ] ) ;
26
121
27
122
const [ anchorEl , setAnchorEl ] = React . useState < null | HTMLElement > ( null ) ;
28
123
@@ -39,7 +134,10 @@ export const ResourceQuotas = () => {
39
134
const open = Boolean ( anchorEl ) ;
40
135
const id = open ? 'simple-popover' : undefined ;
41
136
42
- if ( quotas === null ) {
137
+ const globalRQsDataIsLoading = globalRQs === null && ! globalRQsError ;
138
+ const stageRQsDataIsLoading = stageRQs === null && ! stageRQsError ;
139
+
140
+ if ( globalRQsDataIsLoading || stageRQsDataIsLoading ) {
43
141
return null ;
44
142
}
45
143
@@ -70,40 +168,26 @@ export const ResourceQuotas = () => {
70
168
horizontal : 'right' ,
71
169
} }
72
170
>
73
- < Box sx = { { py : theme . typography . pxToRem ( 20 ) , px : theme . typography . pxToRem ( 30 ) } } >
74
- < Stack direction = "row" spacing = { 5 } >
75
- { Object . entries ( quotas ) . map ( ( [ entity , details ] ) => {
76
- const { hard, used } = details ;
77
- const loadPercentage = Math . floor ( ( used / hard ) * 100 ) ;
78
- const color = getColorByLoadPercentage ( theme , loadPercentage ) ;
79
-
80
- return (
81
- < Box sx = { { flex : '1 1 0' , minWidth : theme . typography . pxToRem ( 100 ) } } >
82
- < Stack alignItems = "center" spacing = { 1 } >
83
- < Typography color = "primary.dark" variant = "subtitle2" >
84
- { entity }
85
- </ Typography >
86
- < Box sx = { { width : '40px' , height : '40px' } } >
87
- < PercentageCircle
88
- data = { [
89
- {
90
- name : 'OK' ,
91
- value : loadPercentage ,
92
- fill : color ,
93
- } ,
94
- ] }
95
- total = { 100 }
96
- size = { 50 }
97
- thickness = { 6 }
98
- />
99
- </ Box >
100
- < Typography color = "primary.dark" variant = "caption" >
101
- { details ?. [ 'used_initial' ] } / { details ?. [ 'hard_initial' ] }
102
- </ Typography >
103
- </ Stack >
104
- </ Box >
105
- ) ;
106
- } ) }
171
+ < Box sx = { { py : theme . typography . pxToRem ( 40 ) , px : theme . typography . pxToRem ( 40 ) } } >
172
+ < Stack spacing = { 5 } >
173
+ < LoadingWrapper isLoading = { globalRQsDataIsLoading } >
174
+ < BorderedSection title = "Global Resource Quotas" >
175
+ < Stack direction = "row" spacing = { 5 } >
176
+ { Object . entries ( globalRQs . quotas ) . map ( ( [ entity , details ] ) => (
177
+ < RQItem key = { `global-${ entity } ` } entity = { entity } details = { details } />
178
+ ) ) }
179
+ </ Stack >
180
+ </ BorderedSection >
181
+ </ LoadingWrapper >
182
+ < LoadingWrapper isLoading = { stageRQsDataIsLoading } >
183
+ < BorderedSection title = "Deployment Flow Resource Quotas" >
184
+ < Stack direction = "row" spacing = { 5 } >
185
+ { Object . entries ( stageRQs . quotas ) . map ( ( [ entity , details ] ) => (
186
+ < RQItem key = { `stage-${ entity } ` } entity = { entity } details = { details } />
187
+ ) ) }
188
+ </ Stack >
189
+ </ BorderedSection >
190
+ </ LoadingWrapper >
107
191
</ Stack >
108
192
</ Box >
109
193
</ Popover >
0 commit comments