@@ -2,82 +2,13 @@ import * as React from 'react';
22import { PageSection , Title } from '@patternfly/react-core' ;
33import { ResourceLink , Timestamp , useK8sWatchResource , useK8sWatchResources , ResourceIcon } from '@openshift-console/dynamic-plugin-sdk' ;
44
5- // Annotation keys - use short keys for safe static access
6- type AnnotationKey = 'ttl' | 'expireAt' | 'status' ;
7-
8- const ANNOTATIONS = {
9- ttl : 'object-lease-controller.ullberg.io/ttl' ,
10- expireAt : 'object-lease-controller.ullberg.io/expire-at' ,
11- status : 'object-lease-controller.ullberg.io/lease-status' ,
12- } as const ;
13-
14- // Helper functions for safe annotation access - avoids dynamic property access security issues
15- const getAnnotation = ( annotations : Record < string , string > | undefined , key : AnnotationKey ) : string | undefined => {
16- if ( ! annotations ) return undefined ;
17- const annotationKey = ANNOTATIONS [ key ] ;
18- return Object . prototype . hasOwnProperty . call ( annotations , annotationKey ) ? annotations [ annotationKey ] : undefined ;
19- } ;
20-
21- const hasLeaseAnnotations = ( annotations : Record < string , string > | undefined ) : boolean => {
22- if ( ! annotations ) return false ;
23- return (
24- Object . prototype . hasOwnProperty . call ( annotations , ANNOTATIONS . ttl ) ||
25- Object . prototype . hasOwnProperty . call ( annotations , ANNOTATIONS . expireAt ) ||
26- Object . prototype . hasOwnProperty . call ( annotations , ANNOTATIONS . status )
27- ) ;
28- } ;
5+ const ANN_TTL = 'object-lease-controller.ullberg.io/ttl' ;
6+ const ANN_EXPIRE_AT = 'object-lease-controller.ullberg.io/expire-at' ;
7+ const ANN_STATUS = 'object-lease-controller.ullberg.io/lease-status' ;
298
309type GVK = { group : string ; version : string ; kind : string } ;
3110type WatchCfg = { groupVersionKind : GVK ; namespaced : boolean ; isList : true ; namespace ?: string } ;
3211
33- // Kubernetes resource metadata type
34- type K8sMetadata = {
35- uid ?: string ;
36- namespace ?: string ;
37- name ?: string ;
38- annotations ?: Record < string , string > ;
39- } ;
40-
41- // Generic Kubernetes resource type
42- type K8sResource = {
43- apiVersion ?: string ;
44- kind ?: string ;
45- metadata ?: K8sMetadata ;
46- } ;
47-
48- // Watch result type from useK8sWatchResources
49- type WatchResult = {
50- data ?: K8sResource [ ] ;
51- loaded : boolean ;
52- loadError ?: Error ;
53- } ;
54-
55- type KindSpec = {
56- singular ?: string ;
57- kind ?: string ;
58- name ?: string ;
59- plural ?: string ;
60- } ;
61-
62- type LeaseControllerSpec = {
63- group ?: string ;
64- version ?: string ;
65- kind ?: string | KindSpec ;
66- } ;
67-
68- type LeaseController = K8sResource & {
69- spec ?: LeaseControllerSpec ;
70- } ;
71-
72- // Item with lease annotations
73- type LeaseItem = {
74- obj : K8sResource & { metadata : K8sMetadata & { name : string } } ;
75- gvk : GVK ;
76- } ;
77-
78- // Monitored GVK type
79- type Monitored = { gvk : GVK ; plural ?: string } ;
80-
8112const LeasesPage = ( ) => {
8213 // Try to use LeaseController CRs if present to determine which Kinds to scan
8314 const leaseControllerGVK : GVK = { group : 'object-lease-controller.ullberg.io' , version : 'v1' , kind : 'LeaseController' } ;
@@ -92,7 +23,7 @@ const LeasesPage = () => {
9223 const cfgs : Record < string , WatchCfg > = { } ;
9324 if ( lcLoaded && ! lcError && Array . isArray ( leaseControllers ) && leaseControllers . length > 0 ) {
9425 const set = new Set < string > ( ) ;
95- leaseControllers . forEach ( ( lc : LeaseController ) => {
26+ leaseControllers . forEach ( ( lc : any ) => {
9627 const g = lc ?. spec ?. group || '' ;
9728 const v = lc ?. spec ?. version as string | undefined ;
9829 const rawKind = lc ?. spec ?. kind ;
@@ -131,36 +62,36 @@ const LeasesPage = () => {
13162 return cfgs ;
13263 } , [ leaseControllers , lcLoaded , lcError ] ) ;
13364
134- const resources = useK8sWatchResources < Record < string , WatchResult > > ( watches ) ;
65+ const resources = useK8sWatchResources ( watches as any ) ;
13566
136- const loaded = React . useMemo ( ( ) => Object . values ( resources ) . every ( ( r : WatchResult ) => r . loaded || r . loadError ) , [ resources ] ) ;
137- const loadError = React . useMemo ( ( ) => Object . values ( resources ) . find ( ( r : WatchResult ) => r . loadError ) ?. loadError , [ resources ] ) ;
67+ const loaded = React . useMemo ( ( ) => Object . values ( resources ) . every ( ( r : any ) => r . loaded || r . loadError ) , [ resources ] ) ;
68+ const loadError = React . useMemo ( ( ) => ( Object . values ( resources ) . find ( ( r : any ) => r . loadError ) as any ) ?. loadError , [ resources ] ) ;
13869
139- const items : LeaseItem [ ] = React . useMemo ( ( ) => {
140- const map = new Map < string , LeaseItem > ( ) ;
141- Object . entries ( resources ) . forEach ( ( [ key , res ] : [ string , WatchResult ] ) => {
70+ const items : Array < { obj : any ; gvk : GVK } > = React . useMemo ( ( ) => {
71+ const map = new Map < string , { obj : any ; gvk : GVK } > ( ) ;
72+ Object . entries ( resources ) . forEach ( ( [ key , res ] : any ) => {
14273 if ( ! res ?. data ) return ;
14374 const [ groupPart , version , kind ] = key . split ( '~' ) ;
14475 const group = groupPart === 'core' ? '' : groupPart ;
14576 const gvk : GVK = { group, version, kind } ;
146- res . data . forEach ( ( obj : K8sResource ) => {
147- const anns = obj ?. metadata ?. annotations ;
148- if ( hasLeaseAnnotations ( anns ) ) {
77+ ( res . data as any [ ] ) . forEach ( ( obj ) => {
78+ const anns = obj ?. metadata ?. annotations || { } ;
79+ if ( anns [ ANN_TTL ] || anns [ ANN_EXPIRE_AT ] || anns [ ANN_STATUS ] ) {
14980 const uid = obj ?. metadata ?. uid ;
150- const name = obj ?. metadata ?. name ;
151- if ( ! name ) return ;
152- const dedupKey = uid ?? `${ obj ?. metadata ?. namespace ?? 'cluster' } - ${ name } ` ;
81+ const dedupKey = uid
82+ ? ` ${ gvk . group } | ${ gvk . version } | ${ gvk . kind } | ${ uid } `
83+ : `${ gvk . group } | ${ gvk . version } | ${ gvk . kind } | ${ obj ?. metadata ?. namespace || '' } | ${ obj ?. metadata ?. name } ` ;
15384 if ( ! map . has ( dedupKey ) ) {
154- map . set ( dedupKey , { obj : obj as LeaseItem [ 'obj' ] , gvk } ) ;
85+ map . set ( dedupKey , { obj, gvk } ) ;
15586 }
15687 }
15788 } ) ;
15889 } ) ;
15990 const out = Array . from ( map . values ( ) ) ;
16091 // Sort by namespace, kind, name
16192 out . sort ( ( a , b ) => {
162- const nsA = a . obj . metadata . namespace ?? '' ;
163- const nsB = b . obj . metadata . namespace ?? '' ;
93+ const nsA = a . obj . metadata . namespace || '' ;
94+ const nsB = b . obj . metadata . namespace || '' ;
16495 if ( nsA !== nsB ) return nsA . localeCompare ( nsB ) ;
16596 if ( a . gvk . kind !== b . gvk . kind ) return a . gvk . kind . localeCompare ( b . gvk . kind ) ;
16697 return a . obj . metadata . name . localeCompare ( b . obj . metadata . name ) ;
@@ -169,10 +100,11 @@ const LeasesPage = () => {
169100 } , [ resources ] ) ;
170101
171102 // Derive the distinct set of monitored GVKs for display
103+ type Monitored = { gvk : GVK ; plural ?: string } ;
172104 const monitored : Monitored [ ] = React . useMemo ( ( ) => {
173105 if ( ! lcLoaded || lcError || ! Array . isArray ( leaseControllers ) ) return [ ] ;
174106 const map = new Map < string , Monitored > ( ) ;
175- leaseControllers . forEach ( ( lc : LeaseController ) => {
107+ leaseControllers . forEach ( ( lc : any ) => {
176108 const g = lc ?. spec ?. group || '' ;
177109 const v = lc ?. spec ?. version as string | undefined ;
178110 const rawKind = lc ?. spec ?. kind ;
@@ -248,9 +180,9 @@ const LeasesPage = () => {
248180 < td >
249181 < ResourceLink groupVersionKind = { gvk } name = { obj . metadata . name } namespace = { obj . metadata . namespace } />
250182 </ td >
251- < td > { getAnnotation ( obj ?. metadata ?. annotations , 'ttl' ) ?? '-' } </ td >
252- < td > < Timestamp timestamp = { getAnnotation ( obj ?. metadata ?. annotations , 'expireAt' ) } /> </ td >
253- < td > { getAnnotation ( obj ?. metadata ?. annotations , 'status' ) ?? '-' } </ td >
183+ < td > { obj ?. metadata ?. annotations ?. [ ANN_TTL ] ?? '-' } </ td >
184+ < td > < Timestamp timestamp = { obj ?. metadata ?. annotations ?. [ ANN_EXPIRE_AT ] } /> </ td >
185+ < td > { obj ?. metadata ?. annotations ?. [ ANN_STATUS ] ?? '-' } </ td >
254186 </ tr >
255187 ) ;
256188 } )
0 commit comments