@@ -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,18 @@ 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
+ }
101
104
}
102
105
103
106
public list ( namespace ?: string | undefined ) : ReadonlyArray < T > {
104
- if ( ! namespace ) {
105
- return this . objects ;
107
+ const namespaceObjects = this . objects . get ( namespace || '' ) ;
108
+ if ( ! namespaceObjects ) {
109
+ return [ ] ;
106
110
}
107
- return this . indexCache [ namespace ] as ReadonlyArray < T > ;
111
+ return Array . from ( namespaceObjects . values ( ) ) ;
108
112
}
109
113
110
114
public latestResourceVersion ( ) : string {
@@ -118,9 +122,9 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
118
122
}
119
123
}
120
124
121
- private async doneHandler ( err : any ) : Promise < any > {
125
+ private async doneHandler ( err : unknown ) : Promise < void > {
122
126
this . _stop ( ) ;
123
- if ( err && err . statusCode === 410 ) {
127
+ if ( err && ( err as { statusCode ?: number } ) . statusCode === 410 ) {
124
128
this . resourceVersion = '' ;
125
129
} else if ( err ) {
126
130
this . callbackCache [ ERROR ] . forEach ( ( elt : ErrorCallback ) => elt ( err ) ) ;
@@ -136,16 +140,8 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
136
140
const result = await promise ;
137
141
const list = result . body ;
138
142
this . objects = deleteItems ( this . objects , list . items , this . callbackCache [ DELETE ] . slice ( ) ) ;
139
- Object . keys ( this . indexCache ) . forEach ( ( key ) => {
140
- const updateObjects = deleteItems ( this . indexCache [ key ] , list . items ) ;
141
- if ( updateObjects . length !== 0 ) {
142
- this . indexCache [ key ] = updateObjects ;
143
- } else {
144
- delete this . indexCache [ key ] ;
145
- }
146
- } ) ;
147
143
this . addOrUpdateItems ( list . items ) ;
148
- this . resourceVersion = list . metadata ! . resourceVersion ! ;
144
+ this . resourceVersion = list . metadata ! . resourceVersion || '' ;
149
145
}
150
146
const queryParams = {
151
147
resourceVersion : this . resourceVersion ,
@@ -172,22 +168,10 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
172
168
this . callbackCache [ ADD ] . slice ( ) ,
173
169
this . callbackCache [ UPDATE ] . slice ( ) ,
174
170
) ;
175
- if ( obj . metadata ! . namespace ) {
176
- this . indexObj ( obj ) ;
177
- }
178
171
} ) ;
179
172
}
180
173
181
- private indexObj ( obj : T ) : void {
182
- let namespaceList = this . indexCache [ obj . metadata ! . namespace ! ] as T [ ] ;
183
- if ( ! namespaceList ) {
184
- namespaceList = [ ] ;
185
- this . indexCache [ obj . metadata ! . namespace ! ] = namespaceList ;
186
- }
187
- addOrUpdateObject ( namespaceList , obj ) ;
188
- }
189
-
190
- private watchHandler ( phase : string , obj : T , watchObj ?: any ) : void {
174
+ private watchHandler ( phase : string , obj : T , watchObj ?: KubernetesObject ) : void {
191
175
switch ( phase ) {
192
176
case 'ADDED' :
193
177
case 'MODIFIED' :
@@ -197,73 +181,97 @@ export class ListWatch<T extends KubernetesObject> implements ObjectCache<T>, In
197
181
this . callbackCache [ ADD ] . slice ( ) ,
198
182
this . callbackCache [ UPDATE ] . slice ( ) ,
199
183
) ;
200
- if ( obj . metadata ! . namespace ) {
201
- this . indexObj ( obj ) ;
202
- }
203
184
break ;
204
185
case 'DELETED' :
205
186
deleteObject ( this . objects , obj , this . callbackCache [ DELETE ] . slice ( ) ) ;
206
- if ( obj . metadata ! . namespace ) {
207
- const namespaceList = this . indexCache [ obj . metadata ! . namespace ! ] as T [ ] ;
208
- if ( namespaceList ) {
209
- deleteObject ( namespaceList , obj ) ;
210
- }
211
- }
212
187
break ;
213
188
case 'BOOKMARK' :
214
189
// nothing to do, here for documentation, mostly.
215
190
break ;
216
191
}
217
192
if ( watchObj && watchObj . metadata ) {
218
- this . resourceVersion = watchObj . metadata . resourceVersion ;
193
+ this . resourceVersion = watchObj . metadata ! . resourceVersion || '' ;
194
+ }
195
+ }
196
+ }
197
+
198
+ // exported for testing
199
+ export function cacheMapFromList < T extends KubernetesObject > ( newObjects : T [ ] ) : CacheMap < T > {
200
+ const objects : CacheMap < T > = new Map ( ) ;
201
+ // build up the new list
202
+ for ( const obj of newObjects ) {
203
+ let namespaceObjects = objects . get ( obj . metadata ! . namespace || '' ) ;
204
+ if ( ! namespaceObjects ) {
205
+ namespaceObjects = new Map ( ) ;
206
+ objects . set ( obj . metadata ! . namespace || '' , namespaceObjects ) ;
219
207
}
208
+
209
+ const name = obj . metadata ! . name || '' ;
210
+ namespaceObjects . set ( name , obj ) ;
220
211
}
212
+ return objects ;
221
213
}
222
214
223
215
// external for testing
224
216
export function deleteItems < T extends KubernetesObject > (
225
- oldObjects : T [ ] ,
217
+ oldObjects : CacheMap < T > ,
226
218
newObjects : T [ ] ,
227
219
deleteCallback ?: Array < ObjectCallback < T > > ,
228
- ) : T [ ] {
229
- return oldObjects . filter ( ( obj : T ) => {
230
- if ( findKubernetesObject ( newObjects , obj ) === - 1 ) {
231
- if ( deleteCallback ) {
232
- deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( obj ) ) ;
220
+ ) : CacheMap < T > {
221
+ const objects = cacheMapFromList ( newObjects ) ;
222
+
223
+ if ( ! deleteCallback ) {
224
+ return objects ;
225
+ }
226
+
227
+ for ( const [ namespace , oldNamespaceObjects ] of oldObjects . entries ( ) ) {
228
+ const newNamespaceObjects = objects . get ( namespace ) ;
229
+ if ( newNamespaceObjects ) {
230
+ for ( const [ name , oldObj ] of oldNamespaceObjects . entries ( ) ) {
231
+ if ( newNamespaceObjects . has ( name ) ) {
232
+ deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( oldObj ) ) ;
233
+ }
233
234
}
234
- return false ;
235
+ } else {
236
+ oldNamespaceObjects . forEach ( ( obj : T ) => {
237
+ deleteCallback . forEach ( ( fn : ObjectCallback < T > ) => fn ( obj ) ) ;
238
+ } ) ;
235
239
}
236
- return true ;
237
- } ) ;
240
+ }
241
+
242
+ return objects ;
238
243
}
239
244
240
245
// Only public for testing.
241
246
export function addOrUpdateObject < T extends KubernetesObject > (
242
- objects : T [ ] ,
247
+ objects : CacheMap < T > ,
243
248
obj : T ,
244
249
addCallback ?: Array < ObjectCallback < T > > ,
245
250
updateCallback ?: Array < ObjectCallback < T > > ,
246
251
) : void {
247
- const ix = findKubernetesObject ( objects , obj ) ;
248
- if ( ix === - 1 ) {
249
- objects . push ( obj ) ;
252
+ let namespaceObjects = objects . get ( obj . metadata ! . namespace || '' ) ;
253
+ if ( ! namespaceObjects ) {
254
+ namespaceObjects = new Map ( ) ;
255
+ objects . set ( obj . metadata ! . namespace || '' , namespaceObjects ) ;
256
+ }
257
+
258
+ const name = obj . metadata ! . name || '' ;
259
+ const found = namespaceObjects . get ( name ) ;
260
+ if ( ! found ) {
261
+ namespaceObjects . set ( name , obj ) ;
250
262
if ( addCallback ) {
251
263
addCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
252
264
}
253
265
} else {
254
- if ( ! isSameVersion ( objects [ ix ] , obj ) ) {
255
- objects [ ix ] = obj ;
266
+ if ( ! isSameVersion ( found , obj ) ) {
267
+ namespaceObjects . set ( name , obj ) ;
256
268
if ( updateCallback ) {
257
269
updateCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
258
270
}
259
271
}
260
272
}
261
273
}
262
274
263
- function isSameObject < T extends KubernetesObject > ( o1 : T , o2 : T ) : boolean {
264
- return o1 . metadata ! . name === o2 . metadata ! . name && o1 . metadata ! . namespace === o2 . metadata ! . namespace ;
265
- }
266
-
267
275
function isSameVersion < T extends KubernetesObject > ( o1 : T , o2 : T ) : boolean {
268
276
return (
269
277
o1 . metadata ! . resourceVersion !== undefined &&
@@ -272,23 +280,26 @@ function isSameVersion<T extends KubernetesObject>(o1: T, o2: T): boolean {
272
280
) ;
273
281
}
274
282
275
- function findKubernetesObject < T extends KubernetesObject > ( objects : T [ ] , obj : T ) : number {
276
- return objects . findIndex ( ( elt : T ) => {
277
- return isSameObject ( elt , obj ) ;
278
- } ) ;
279
- }
280
-
281
283
// Public for testing.
282
284
export function deleteObject < T extends KubernetesObject > (
283
- objects : T [ ] ,
285
+ objects : CacheMap < T > ,
284
286
obj : T ,
285
287
deleteCallback ?: Array < ObjectCallback < T > > ,
286
288
) : void {
287
- const ix = findKubernetesObject ( objects , obj ) ;
288
- if ( ix !== - 1 ) {
289
- objects . splice ( ix , 1 ) ;
289
+ const namespace = obj . metadata ! . namespace || '' ;
290
+ const name = obj . metadata ! . name || '' ;
291
+
292
+ const namespaceObjects = objects . get ( namespace ) ;
293
+ if ( ! namespaceObjects ) {
294
+ return ;
295
+ }
296
+ const deleted = namespaceObjects . delete ( name ) ;
297
+ if ( deleted ) {
290
298
if ( deleteCallback ) {
291
299
deleteCallback . forEach ( ( elt : ObjectCallback < T > ) => elt ( obj ) ) ;
292
300
}
301
+ if ( namespaceObjects . size === 0 ) {
302
+ objects . delete ( namespace ) ;
303
+ }
293
304
}
294
305
}
0 commit comments