@@ -226,13 +226,11 @@ class DockerNetworkMonitor {
226
226
}
227
227
228
228
private async refreshContainerHosts ( container : Docker . ContainerInspectInfo ) {
229
- this . setContainerMapping ( container . Id , {
230
- // The container can send itself requests to its own hostname:
231
- [ container . Config . Hostname ] : new Set ( container . NetworkSettings . IPAddress ) ,
232
- // It can also have hosts configured via --add-host=host:ip, which adds them to
229
+ this . setContainerMapping ( container . Id ,
230
+ // Containers may have hosts configured via --add-host=host:ip, which adds them to
233
231
// /etc/hosts. Note that we ignore conflicts here, and just pick the first result,
234
232
// which seems to match how resolution against /etc/hosts works in general.
235
- ... ( _ ( container . HostConfig . ExtraHosts ?? [ ] )
233
+ _ ( container . HostConfig . ExtraHosts ?? [ ] )
236
234
. reverse ( ) // We want first conflict to win, not last
237
235
. map ( ( hostPair ) => hostPair . split ( ':' ) )
238
236
. keyBy ( ( hostParts ) => hostParts [ 0 ] )
@@ -245,8 +243,7 @@ class DockerNetworkMonitor {
245
243
: new Set ( [ address ] )
246
244
)
247
245
. valueOf ( )
248
- )
249
- } ) ;
246
+ ) ;
250
247
}
251
248
252
249
private async refreshAllNetworks ( ) {
@@ -273,34 +270,70 @@ class DockerNetworkMonitor {
273
270
return { } ;
274
271
}
275
272
276
- // Build a map of each container's aliases to its IP on this network
277
- return networkContainers . reduce ( ( aliasMap , container ) => {
278
- const networkConfig : Docker . EndpointSettings | undefined =
279
- _ . find ( container . NetworkSettings . Networks , { NetworkID : networkId } ) ;
280
-
281
- if ( ! networkConfig || ! networkConfig . IPAddress ) return aliasMap ;
282
-
283
- /*
284
- * What names are resolveable on a network?
285
- *
286
- * On a default bridge network: hostnames are self-resolveable (handled by
287
- * refreshContainerHosts), and that's it unless links are used. No aliases are defined.
288
- *
289
- * On a custom bridge network: hostnames are fully resolveable, as are container
290
- * ids, plus any custom aliases defined in network config. All defined in Aliases.
291
- *
292
- * On a host network: everything resolves as on the host (so we do nothing). Since
293
- * there's no actual network involved, we never get here anyway.
294
- *
295
- * Overlay etc out of scope for now.
296
- */
297
-
298
- const aliases = networkConfig . Aliases ?? [ ] ;
299
-
300
- aliases . forEach ( ( alias ) => {
301
- if ( ! aliasMap [ alias ] ) aliasMap [ alias ] = new Set ( ) ;
302
- aliasMap [ alias ] . add ( networkConfig . IPAddress ! ) ;
303
- } ) ;
273
+ /*
274
+ * So, what names are resolveable on a network?
275
+ *
276
+ * On a default bridge network: hostnames are self-resolveable,
277
+ * and that's it unless links are used. No aliases are defined.
278
+ *
279
+ * On a custom bridge network: hostnames are fully resolveable, as are container
280
+ * ids, plus any custom aliases defined in network config. All defined in Aliases.
281
+ *
282
+ * On a host network: everything resolves as on the host (so we do nothing). Since
283
+ * there's no actual network involved, we never get here anyway.
284
+ *
285
+ * On any network: linked containers can be referenced by their real name or by
286
+ * their link alias name.
287
+ *
288
+ * Overlay etc out of scope for now.
289
+ */
290
+
291
+ const aliasPairs = await Promise . all (
292
+ networkContainers . map ( async ( container ) : Promise < Array < readonly [ string , string ] > > => {
293
+ const networkConfig : Docker . EndpointSettings | undefined =
294
+ _ . find ( container . NetworkSettings . Networks , { NetworkID : networkId } ) ;
295
+
296
+ if ( ! networkConfig || ! networkConfig . IPAddress ) return [ ] ;
297
+
298
+ const aliasNames = [
299
+ ...( networkConfig . Aliases || [ ] ) ,
300
+ // We resolve hostnames here, not on container creation, because the IP isn't
301
+ // set at container creation time.
302
+ container . Config . Hostname
303
+ ] ;
304
+
305
+ const aliasMap = aliasNames . map ( ( alias ) => [ alias , networkConfig . IPAddress ! ] as const ) ;
306
+
307
+ // This queries afresh for each linked container's info. Ignoring for now, since it's very cheap
308
+ // and container links are a legacy Docker feature anyway.
309
+ const linkStrings : string [ ] = container . HostConfig . Links || [ ] ;
310
+ const linkMap = await Promise . all ( linkStrings . map ( async ( link ) => {
311
+ // Aliases are of the form:
312
+ // /compose_default-service-a_1_HTK8000:/compose_linked-service-b_1_HTK8000/a
313
+ // I.e. service-a is linked by service-b with alias 'a'.
314
+ const endOfContainerName = link . indexOf ( ':/' ) ;
315
+ const aliasIndex = link . lastIndexOf ( '/' ) ;
316
+
317
+ const linkedContainerName = link . slice ( 1 , endOfContainerName ) ;
318
+ const linkAlias = link . slice ( aliasIndex + 1 ) ; // +1 to drop leading slash
319
+
320
+ const linkedContainer = await this . docker . getContainer ( linkedContainerName ) . inspect ( ) ;
321
+ const linkedContainerIp = linkedContainer . NetworkSettings . Networks [ networkId ] ?. IPAddress ||
322
+ linkedContainer . NetworkSettings . IPAddress ;
323
+ return [ linkAlias , linkedContainerIp ] as const ;
324
+ } ) ) ;
325
+
326
+ return [
327
+ ...aliasMap ,
328
+ ...linkMap
329
+ ] ;
330
+ } )
331
+ ) ;
332
+
333
+ // Turn those arrays of pairs into a single string -> set map.
334
+ return _ . flatten ( aliasPairs ) . reduce ( ( aliasMap , [ alias , target ] ) => {
335
+ if ( ! aliasMap [ alias ] ) aliasMap [ alias ] = new Set ( ) ;
336
+ aliasMap [ alias ] . add ( target ) ;
304
337
return aliasMap ;
305
338
} , { } as { [ alias : string ] : Set < string > } ) ;
306
339
}
0 commit comments