11import archiver from "archiver" ;
22import AsyncLock from "async-lock" ;
3- import { Container , ContainerCreateOptions , HostConfig } from "dockerode" ;
3+ import { Container , ContainerCreateOptions , ContainerInspectInfo , HostConfig } from "dockerode" ;
44import { Readable } from "stream" ;
5- import { containerLog , hash , log , toNanos } from "../common" ;
5+ import { containerLog , hash , IntervalRetry , log , toNanos } from "../common" ;
66import { ContainerRuntimeClient , getContainerRuntimeClient , ImageName } from "../container-runtime" ;
77import { CONTAINER_STATUSES } from "../container-runtime/clients/container/types" ;
88import { StartedNetwork } from "../network/network" ;
@@ -141,8 +141,7 @@ export class GenericContainer implements TestContainer {
141141 if ( ! inspectResult . State . Running ) {
142142 log . debug ( "Reused container is not running, attempting to start it" ) ;
143143 await client . container . start ( container ) ;
144- // Refetch the inspect result to get the updated state
145- inspectResult = await client . container . inspect ( container ) ;
144+ inspectResult = ( await this . inspectContainer ( client , container ) ) . inspectResult ;
146145 }
147146
148147 const mappedInspectResult = mapInspectResult ( inspectResult ) ;
@@ -196,8 +195,7 @@ export class GenericContainer implements TestContainer {
196195 await client . container . start ( container ) ;
197196 log . info ( `Started container for image "${ this . createOpts . Image } "` , { containerId : container . id } ) ;
198197
199- const inspectResult = await client . container . inspect ( container ) ;
200- const mappedInspectResult = mapInspectResult ( inspectResult ) ;
198+ const { inspectResult, mappedInspectResult } = await this . inspectContainer ( client , container ) ;
201199 const boundPorts = BoundPorts . fromInspectResult ( client . info . containerRuntime . hostIps , mappedInspectResult ) . filter (
202200 this . exposedPorts
203201 ) ;
@@ -241,6 +239,48 @@ export class GenericContainer implements TestContainer {
241239 return startedContainer ;
242240 }
243241
242+ private async inspectContainer (
243+ client : ContainerRuntimeClient ,
244+ container : Container
245+ ) : Promise < {
246+ inspectResult : ContainerInspectInfo ;
247+ mappedInspectResult : InspectResult ;
248+ } > {
249+ const containerInspectRetry = await new IntervalRetry <
250+ {
251+ inspectResult : ContainerInspectInfo ;
252+ mappedInspectResult : InspectResult ;
253+ } ,
254+ Error
255+ > ( 100 ) . retryUntil (
256+ async ( ) => {
257+ const inspectResult = await client . container . inspect ( container ) ;
258+ const mappedInspectResult = mapInspectResult ( inspectResult ) ;
259+ return { inspectResult, mappedInspectResult } ;
260+ } ,
261+ ( { mappedInspectResult } ) =>
262+ this . exposedPorts
263+ . map ( ( exposedPort ) => getContainerPort ( exposedPort ) )
264+ . every (
265+ ( exposedPort ) =>
266+ mappedInspectResult . ports [ exposedPort ] . length > 0 &&
267+ mappedInspectResult . ports [ exposedPort ] . every ( ( { hostPort } ) => hostPort !== undefined )
268+ ) ,
269+ ( ) => {
270+ const message = `Container did not expose all ports after starting` ;
271+ log . error ( message , { containerId : container . id } ) ;
272+ return new Error ( message ) ;
273+ } ,
274+ 3000
275+ ) ;
276+
277+ if ( containerInspectRetry instanceof Error ) {
278+ throw containerInspectRetry ;
279+ }
280+
281+ return containerInspectRetry ;
282+ }
283+
244284 private async connectContainerToPortForwarder ( client : ContainerRuntimeClient , container : Container ) {
245285 const portForwarder = await PortForwarderInstance . getInstance ( ) ;
246286 const portForwarderNetworkId = portForwarder . getNetworkId ( ) ;
@@ -361,7 +401,7 @@ export class GenericContainer implements TestContainer {
361401 public withExposedPorts ( ...ports : PortWithOptionalBinding [ ] ) : this {
362402 const exposedPorts : { [ port : string ] : Record < string , never > } = { } ;
363403 for ( const exposedPort of ports ) {
364- exposedPorts [ getContainerPort ( exposedPort ) . toString ( ) ] = { } ;
404+ exposedPorts [ ` ${ getContainerPort ( exposedPort ) . toString ( ) } /tcp` ] = { } ;
365405 }
366406
367407 this . exposedPorts = [ ...this . exposedPorts , ...ports ] ;
@@ -373,9 +413,9 @@ export class GenericContainer implements TestContainer {
373413 const portBindings : Record < string , Array < Record < string , string > > > = { } ;
374414 for ( const exposedPort of ports ) {
375415 if ( hasHostBinding ( exposedPort ) ) {
376- portBindings [ exposedPort . container ] = [ { HostPort : exposedPort . host . toString ( ) } ] ;
416+ portBindings [ ` ${ exposedPort . container } /tcp` ] = [ { HostPort : exposedPort . host . toString ( ) } ] ;
377417 } else {
378- portBindings [ exposedPort ] = [ { HostPort : "0" } ] ;
418+ portBindings [ ` ${ exposedPort } /tcp` ] = [ { HostPort : "0" } ] ;
379419 }
380420 }
381421
0 commit comments