@@ -20,19 +20,23 @@ export interface ObjectCache<T> {
20
20
list ( namespace ?: string ) : ReadonlyArray < T > ;
21
21
}
22
22
23
+ // exported for testing
24
+ export type CacheMap < T extends KubernetesObject > = Map < string , Map < string , T > > ;
25
+
23
26
export class ListWatch < T extends KubernetesObject > implements ObjectCache < T > , Informer < T > {
24
- private objects : T [ ] = [ ] ;
27
+ private objects : CacheMap < T > = new Map ( ) ;
25
28
private resourceVersion : string ;
26
- private readonly indexCache : { [ key : string ] : T [ ] } = { } ;
27
- private readonly callbackCache : { [ key : string ] : Array < ObjectCallback < T > | ErrorCallback > } = { } ;
29
+ private readonly callbackCache : {
30
+ [ key : string ] : Array < ObjectCallback < T > | ErrorCallback > ;
31
+ } = { } ;
28
32
private request : RequestResult | undefined ;
29
- private stopped : boolean = false ;
33
+ private stopped = false ;
30
34
31
35
public constructor (
32
36
private readonly path : string ,
33
37
private readonly watch : Watch ,
34
38
private readonly listFn : ListPromise < T > ,
35
- autoStart : boolean = true ,
39
+ autoStart = true ,
36
40
private readonly labelSelector ?: string ,
37
41
) {
38
42
this . callbackCache [ ADD ] = [ ] ;
@@ -93,18 +97,19 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
93
97
}
94
98
95
99
public get ( name : string , namespace ?: string ) : T | undefined {
96
- return this . objects . find (
97
- ( obj : T ) : boolean => {
98
- return obj . metadata ! . name === name && ( ! namespace || obj . metadata ! . namespace === namespace ) ;
99
- } ,
100
- ) ;
100
+ const nsObjects = this . objects . get ( namespace || '' ) ;
101
+ if ( nsObjects ) {
102
+ return nsObjects . get ( name ) ;
103
+ }
104
+ return undefined ;
101
105
}
102
106
103
107
public list ( namespace ?: string | undefined ) : ReadonlyArray < T > {
104
- if ( ! namespace ) {
105
- return this . objects ;
108
+ const namespaceObjects = this . objects . get ( namespace || '' ) ;
109
+ if ( ! namespaceObjects ) {
110
+ return [ ] ;
106
111
}
107
- return this . indexCache [ namespace ] as ReadonlyArray < T > ;
112
+ return Array . from ( namespaceObjects . values ( ) ) ;
108
113
}
109
114
110
115
public latestResourceVersion ( ) : string {
@@ -118,7 +123,7 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
118
123
}
119
124
}
120
125
121
- private async doneHandler ( err : any ) : Promise < any > {
126
+ private async doneHandler ( err : unknown ) : Promise < void > {
122
127
this . _stop ( ) ;
123
128
if (
124
129
err &&
@@ -139,16 +144,8 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
139
144
const result = await promise ;
140
145
const list = result . body ;
141
146
this . objects = deleteItems ( this . objects , list . items , this . callbackCache [ DELETE ] . slice ( ) ) ;
142
- Object . keys ( this . indexCache ) . forEach ( ( key ) => {
143
- const updateObjects = deleteItems ( this . indexCache [ key ] , list . items ) ;
144
- if ( updateObjects . length !== 0 ) {
145
- this . indexCache [ key ] = updateObjects ;
146
- } else {
147
- delete this . indexCache [ key ] ;
148
- }
149
- } ) ;
150
147
this . addOrUpdateItems ( list . items ) ;
151
- this . resourceVersion = list . metadata ! . resourceVersion ! ;
148
+ this . resourceVersion = list . metadata ! . resourceVersion || '' ;
152
149
}
153
150
const queryParams = {
154
151
resourceVersion : this . resourceVersion ,
@@ -175,21 +172,9 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
175
172
this . callbackCache [ ADD ] . slice ( ) ,
176
173
this . callbackCache [ UPDATE ] . slice ( ) ,
177
174
) ;
178
- if ( obj . metadata ! . namespace ) {
179
- this . indexObj ( obj ) ;
180
- }
181
175
} ) ;
182
176
}
183
177
184
- private indexObj ( obj : T ) : void {
185
- let namespaceList = this . indexCache [ obj . metadata ! . namespace ! ] as T [ ] ;
186
- if ( ! namespaceList ) {
187
- namespaceList = [ ] ;
188
- this . indexCache [ obj . metadata ! . namespace ! ] = namespaceList ;
189
- }
190
- addOrUpdateObject ( namespaceList , obj ) ;
191
- }
192
-
193
178
private async watchHandler (
194
179
phase : string ,
195
180
obj : T ,
@@ -204,18 +189,9 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
204
189
this . callbackCache [ ADD ] . slice ( ) ,
205
190
this . callbackCache [ UPDATE ] . slice ( ) ,
206
191
) ;
207
- if ( obj . metadata ! . namespace ) {
208
- this . indexObj ( obj ) ;
209
- }
210
192
break ;
211
193
case 'DELETED' :
212
194
deleteObject ( this . objects , obj , this . callbackCache [ DELETE ] . slice ( ) ) ;
213
- if ( obj . metadata ! . namespace ) {
214
- const namespaceList = this . indexCache [ obj . metadata ! . namespace ! ] as T [ ] ;
215
- if ( namespaceList ) {
216
- deleteObject ( namespaceList , obj ) ;
217
- }
218
- }
219
195
break ;
220
196
case 'BOOKMARK' :
221
197
// nothing to do, here for documentation, mostly.
@@ -228,50 +204,83 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
228
204
}
229
205
}
230
206
207
+ // exported for testing
208
+ export function cacheMapFromList < T extends KubernetesObject > ( newObjects : T [ ] ) : CacheMap < T > {
209
+ const objects : CacheMap < T > = new Map ( ) ;
210
+ // build up the new list
211
+ for ( const obj of newObjects ) {
212
+ let namespaceObjects = objects . get ( obj . metadata ! . namespace || '' ) ;
213
+ if ( ! namespaceObjects ) {
214
+ namespaceObjects = new Map ( ) ;
215
+ objects . set ( obj . metadata ! . namespace || '' , namespaceObjects ) ;
216
+ }
217
+
218
+ const name = obj . metadata ! . name || '' ;
219
+ namespaceObjects . set ( name , obj ) ;
220
+ }
221
+ return objects ;
222
+ }
223
+
231
224
// external for testing
232
225
export function deleteItems < T extends KubernetesObject > (
233
- oldObjects : T [ ] ,
226
+ oldObjects : CacheMap < T > ,
234
227
newObjects : T [ ] ,
235
228
deleteCallback ?: Array < ObjectCallback < T > > ,
236
- ) : T [ ] {
237
- return oldObjects . filter ( ( obj : T ) => {
238
- if ( findKubernetesObject ( newObjects , obj ) === - 1 ) {
239
- if ( deleteCallback ) {
240
- deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( obj ) ) ;
229
+ ) : CacheMap < T > {
230
+ const objects = cacheMapFromList ( newObjects ) ;
231
+
232
+ if ( ! deleteCallback ) {
233
+ return objects ;
234
+ }
235
+
236
+ for ( const [ namespace , oldNamespaceObjects ] of oldObjects . entries ( ) ) {
237
+ const newNamespaceObjects = objects . get ( namespace ) ;
238
+ if ( newNamespaceObjects ) {
239
+ for ( const [ name , oldObj ] of oldNamespaceObjects . entries ( ) ) {
240
+ if ( newNamespaceObjects . has ( name ) ) {
241
+ deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( oldObj ) ) ;
242
+ }
241
243
}
242
- return false ;
244
+ } else {
245
+ oldNamespaceObjects . forEach ( ( obj : T ) => {
246
+ deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( obj ) ) ;
247
+ } ) ;
243
248
}
244
- return true ;
245
- } ) ;
249
+ }
250
+
251
+ return objects ;
246
252
}
247
253
248
254
// Only public for testing.
249
255
export function addOrUpdateObject < T extends KubernetesObject > (
250
- objects : T [ ] ,
256
+ objects : CacheMap < T > ,
251
257
obj : T ,
252
258
addCallback ?: Array < ObjectCallback < T > > ,
253
259
updateCallback ?: Array < ObjectCallback < T > > ,
254
260
) : void {
255
- const ix = findKubernetesObject ( objects , obj ) ;
256
- if ( ix === - 1 ) {
257
- objects . push ( obj ) ;
261
+ let namespaceObjects = objects . get ( obj . metadata ! . namespace || '' ) ;
262
+ if ( ! namespaceObjects ) {
263
+ namespaceObjects = new Map ( ) ;
264
+ objects . set ( obj . metadata ! . namespace || '' , namespaceObjects ) ;
265
+ }
266
+
267
+ const name = obj . metadata ! . name || '' ;
268
+ const found = namespaceObjects . get ( name ) ;
269
+ if ( ! found ) {
270
+ namespaceObjects . set ( name , obj ) ;
258
271
if ( addCallback ) {
259
272
addCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
260
273
}
261
274
} else {
262
- if ( ! isSameVersion ( objects [ ix ] , obj ) ) {
263
- objects [ ix ] = obj ;
275
+ if ( ! isSameVersion ( found , obj ) ) {
276
+ namespaceObjects . set ( name , obj ) ;
264
277
if ( updateCallback ) {
265
278
updateCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
266
279
}
267
280
}
268
281
}
269
282
}
270
283
271
- function isSameObject < T extends KubernetesObject > ( o1 : T , o2 : T ) : boolean {
272
- return o1 . metadata ! . name === o2 . metadata ! . name && o1 . metadata ! . namespace === o2 . metadata ! . namespace ;
273
- }
274
-
275
284
function isSameVersion < T extends KubernetesObject > ( o1 : T , o2 : T ) : boolean {
276
285
return (
277
286
o1 . metadata ! . resourceVersion !== undefined &&
@@ -280,23 +289,26 @@ function isSameVersion<T extends KubernetesObject>(o1: T, o2: T): boolean {
280
289
) ;
281
290
}
282
291
283
- function findKubernetesObject < T extends KubernetesObject > ( objects : T [ ] , obj : T ) : number {
284
- return objects . findIndex ( ( elt : T ) => {
285
- return isSameObject ( elt , obj ) ;
286
- } ) ;
287
- }
288
-
289
292
// Public for testing.
290
293
export function deleteObject < T extends KubernetesObject > (
291
- objects : T [ ] ,
294
+ objects : CacheMap < T > ,
292
295
obj : T ,
293
296
deleteCallback ?: Array < ObjectCallback < T > > ,
294
297
) : void {
295
- const ix = findKubernetesObject ( objects , obj ) ;
296
- if ( ix !== - 1 ) {
297
- objects . splice ( ix , 1 ) ;
298
+ const namespace = obj . metadata ! . namespace || '' ;
299
+ const name = obj . metadata ! . name || '' ;
300
+
301
+ const namespaceObjects = objects . get ( namespace ) ;
302
+ if ( ! namespaceObjects ) {
303
+ return ;
304
+ }
305
+ const deleted = namespaceObjects . delete ( name ) ;
306
+ if ( deleted ) {
298
307
if ( deleteCallback ) {
299
308
deleteCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
300
309
}
310
+ if ( namespaceObjects . size === 0 ) {
311
+ objects . delete ( namespace ) ;
312
+ }
301
313
}
302
314
}
0 commit comments