testcontainers / testcontainers-node

Testcontainers is a NodeJS library that supports tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
https://testcontainers.com
MIT License
1.94k stars 195 forks source link

Intermittently getting wrong mapped port when using multiple networks #859

Open LiamLamb opened 2 weeks ago

LiamLamb commented 2 weeks ago

Expected Behaviour When a container is part of multiple networks, I should be able to consistently get the right port using getMappedPort.

Actual Behaviour In these conditions, getMappedPort intermittently returns a port different to that in the Ports block of NetworkSettings, leading to clients using that port to fail to connect to their destination.

Testcontainer Logs Logs of the failing run:

Run 10
Creating network a
  testcontainers [INFO] Starting network "6f8b2d805b8c"... +0ms
  testcontainers [DEBUG] Creating network "6f8b2d805b8c"... +0ms
  testcontainers [DEBUG] Created network "6f8b2d805b8c" +140ms
  testcontainers [INFO] Started network "6f8b2d805b8c" with ID "7d5be5268d87908a2d467086a20c8d6d0773cdd7c9c901b3417d68ef48ccf42e" +0ms
Creating network b
  testcontainers [INFO] Starting network "202a305aaf98"... +0ms
  testcontainers [DEBUG] Creating network "202a305aaf98"... +0ms
  testcontainers [DEBUG] Created network "202a305aaf98" +141ms
  testcontainers [INFO] Started network "202a305aaf98" with ID "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820" +0ms
Starting ping-pong container
  testcontainers [DEBUG] Image "briceburg/ping-pong:latest" already exists +1ms
  testcontainers [DEBUG] Creating container for image "briceburg/ping-pong:latest"... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Created container for image "briceburg/ping-pong:latest" +44ms
  testcontainers [INFO] [ec638beb98c3] Starting container for image "briceburg/ping-pong:latest"... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Starting container... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Started container +282ms
  testcontainers [INFO] [ec638beb98c3] Started container for image "briceburg/ping-pong:latest" +1ms
  testcontainers [DEBUG] [ec638beb98c3] Inspecting container... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Inspected container +4ms
  testcontainers [DEBUG] [ec638beb98c3] Fetching container logs... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Demuxing stream... +10ms
  testcontainers [DEBUG] [ec638beb98c3] Demuxed stream +0ms
  testcontainers [DEBUG] [ec638beb98c3] Fetched container logs +0ms
  testcontainers [DEBUG] [ec638beb98c3] Waiting for container to be ready... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Waiting for host port 55528... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Waiting for internal port 80... +1ms
  testcontainers:containers [ec638beb98c3]  +2s
  testcontainers:containers [ec638beb98c3] [npm update] +0ms
  testcontainers [DEBUG] [ec638beb98c3] Host port 55528 ready +2ms
  testcontainers [DEBUG] [ec638beb98c3] Host port wait strategy complete +0ms
  testcontainers:containers [ec638beb98c3] ping-pong@0.0.1 /app +6s
  testcontainers:containers [ec638beb98c3] `-- express@4.21.1 +0ms
  testcontainers:containers [ec638beb98c3]  +0ms
  testcontainers:containers [ec638beb98c3] npm WARN ping-pong@0.0.1 No repository field. +5ms
  testcontainers:containers [ec638beb98c3] npm WARN ping-pong@0.0.1 No license field. +0ms
  testcontainers:containers [ec638beb98c3]  +20ms
  testcontainers:containers [ec638beb98c3] [ping-pong] +1ms
  testcontainers:containers [ec638beb98c3] ping-pong listening on http://localhost:80 +240ms
  testcontainers [DEBUG] [ec638beb98c3] Internal port 80 ready +7s
  testcontainers [INFO] [ec638beb98c3] Container is ready +0ms
  testcontainers [DEBUG] [ec638beb98c3] Getting container by ID... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Got container by ID +0ms
  testcontainers [DEBUG] Getting network by ID... +0ms
  testcontainers [DEBUG] Got network by ID +0ms
Connecting container to network b
  testcontainers [DEBUG] [ec638beb98c3] Connecting to network "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820"... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Connected to network "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820"... +110ms
AxiosError: read ECONNRESET
    at Function.AxiosError.from (/Users/liam/Projects/Github/testcontainers-network/node_modules/axios/lib/core/AxiosError.js:92:14)
    at RedirectableRequest.handleRequestError (/Users/liam/Projects/Github/testcontainers-network/node_modules/axios/lib/adapters/http.js:620:25)
    at RedirectableRequest.emit (node:events:518:28)
    at RedirectableRequest.emit (node:domain:489:12)
    at ClientRequest.eventHandlers.<computed> (/Users/liam/Projects/Github/testcontainers-network/node_modules/follow-redirects/index.js:49:24)
    at ClientRequest.emit (node:events:518:28)
    at ClientRequest.emit (node:domain:489:12)
    at emitErrorEvent (node:_http_client:103:11)
    at Socket.socketErrorListener (node:_http_client:506:5)
    at Socket.emit (node:events:518:28)
    at Axios.request (/Users/liam/Projects/Github/testcontainers-network/node_modules/axios/lib/core/Axios.js:45:41)
    at processTicksAndRejections (node:internal/process/task_queues:105:5) {
  syscall: 'read',
  code: 'ECONNRESET',
  errno: -54,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http', 'fetch' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: Object [AxiosHeaders] {
      Accept: 'application/json, text/plain, */*',
      'Content-Type': undefined,
      'User-Agent': 'axios/1.7.7',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    baseURL: 'http://localhost:55528',
    method: 'get',
    url: '/ping',
    data: undefined
  },
  request: <ref *1> Writable {
    _events: {
      close: undefined,
      error: [Function: handleRequestError],
      prefinish: undefined,
      finish: undefined,
      drain: undefined,
      response: [Function: handleResponse],
      socket: [Function: handleRequestSocket]
    },
    _writableState: WritableState {
      highWaterMark: 65536,
      length: 0,
      corked: 0,
      onwrite: [Function: bound onwrite],
      writelen: 0,
      bufferedIndex: 0,
      pendingcb: 0,
      [Symbol(kState)]: 17580812,
      [Symbol(kBufferedValue)]: null
    },
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: Infinity,
      protocol: 'http:',
      path: '/ping',
      method: 'GET',
      headers: [Object: null prototype],
      agents: [Object],
      auth: undefined,
      family: undefined,
      beforeRedirect: [Function: dispatchBeforeRedirect],
      beforeRedirects: [Object],
      hostname: 'localhost',
      port: '55528',
      agent: undefined,
      nativeProtocols: [Object],
      pathname: '/ping'
    },
    _ended: true,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 0,
    _requestBodyBuffers: [],
    _eventsCount: 3,
    _onNativeResponse: [Function (anonymous)],
    _currentRequest: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: false,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 0,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      _header: 'GET /ping HTTP/1.1\r\n' +
        'Accept: application/json, text/plain, */*\r\n' +
        'User-Agent: axios/1.7.7\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: localhost:55528\r\n' +
        'Connection: keep-alive\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'GET',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/ping',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: [Function: emitRequestTimeout],
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'localhost',
      protocol: 'http:',
      _redirectable: [Circular *1],
      [Symbol(shapeMode)]: false,
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kChunkedBuffer)]: [],
      [Symbol(kChunkedLength)]: 0,
      [Symbol(kSocket)]: [Socket],
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 65536,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    _currentUrl: 'http://localhost:55528/ping',
    [Symbol(shapeMode)]: true,
    [Symbol(kCapture)]: false
  },
  cause: Error: read ECONNRESET
      at TCP.onStreamRead (node:internal/stream_base_commons:216:20) {
    errno: -54,
    code: 'ECONNRESET',
    syscall: 'read'
  }
}
  testcontainers [DEBUG] [ec638beb98c3] Inspecting container... +23ms
  testcontainers [DEBUG] [ec638beb98c3] Inspected container +5ms
Expected port: 55529 vs actual port: 55528
  testcontainers [INFO] [ec638beb98c3] Stopping container... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Stopping container... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Stopped container +283ms
  testcontainers [DEBUG] [ec638beb98c3] Removing container... +0ms
  testcontainers [DEBUG] [ec638beb98c3] Removed container +49ms
  testcontainers [INFO] [ec638beb98c3] Stopped container +0ms
  testcontainers [INFO] Stopping network with ID "7d5be5268d87908a2d467086a20c8d6d0773cdd7c9c901b3417d68ef48ccf42e"... +0ms
  testcontainers [DEBUG] Removing network "7d5be5268d87908a2d467086a20c8d6d0773cdd7c9c901b3417d68ef48ccf42e"... +0ms
  testcontainers [DEBUG] Removed network "7d5be5268d87908a2d467086a20c8d6d0773cdd7c9c901b3417d68ef48ccf42e"... +106ms
  testcontainers [INFO] Stopped network with ID "7d5be5268d87908a2d467086a20c8d6d0773cdd7c9c901b3417d68ef48ccf42e" +0ms
  testcontainers [INFO] Stopping network with ID "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820"... +0ms
  testcontainers [DEBUG] Removing network "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820"... +0ms
  testcontainers [DEBUG] Removed network "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820"... +139ms
  testcontainers [INFO] Stopped network with ID "33a686ac3a0725b8facfe86b90d31df75daaa56ca9882f9c6b13e1912f731820" +0ms

Steps to Reproduce I have created a small repository here to reproduce the issue. There's a ReadMe included with the necessary instructions to run the project and see the issue (note that it's intermittent, so you might need to run it more than once)

Environment Information I am seeing this in multiple environments (mac & windows)

Mac Information

ProductName:            macOS
ProductVersion:         13.6
BuildVersion:           22G120

npm doctor:

❯ npm doctor
Connecting to the registry
Ok

Checking npm version
Ok
current: v10.9.0, latest: v10.9.0

Checking node version
Ok
current: v22.11.0, recommended: v22.11.0

Checking configured npm registry
Ok
using default registry (https://registry.npmjs.org/)

Checking for git executable in PATH
Ok
/usr/bin/git

Checking for global bin folder in PATH
Ok
/Users/liam/.nvm/versions/node/v22.11.0/bin

Checking permissions on cached files (this may take awhile)
Ok

Checking permissions on local node_modules (this may take awhile)
Ok

Checking permissions on global node_modules (this may take awhile)
Ok

Checking permissions on local bin folder
Ok

Checking permissions on global bin folder
Ok

Verifying cache contents (this may take awhile)
npm warn doctor verifyCachedFiles Content garbage-collected: 58 (106816249 bytes)
npm warn doctor verifyCachedFiles Cache issues have been fixed
Ok
verified 1970 tarballs