packages feed

testcontainers 0.5.3.0 → 0.5.4.0

raw patch · 6 files changed

+97/−48 lines, 6 filesdep ~tasty-hspecPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: tasty-hspec

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for testcontainer-hs +## 0.5.4.0 -- 2026-07-06++* Added support for Windows containers (@nikita-volkov, https://github.com/testcontainers/testcontainers-hs/pull/72)++* Default image versions updated for Redis, Mongo, RabbitMQ, Nginx, Jaeger and Postgres (@nikita-volkov, https://github.com/testcontainers/testcontainers-hs/pull/72)+ ## 0.5.3.0 -- 2026-03-31  * Removed dependency on `optics-core` and `aeson-optics` (@alexbiehl, https://github.com/testcontainers/testcontainers-hs/pull/70)
src/TestContainers/Docker.hs view
@@ -670,11 +670,24 @@ -- @since 0.5.0.0 createRyukReaper :: TestContainer Reaper createRyukReaper = do-  dockerSocketLocation <--    liftIO $-      lookupEnv "DOCKER_HOST"-        <&> (>>= stripPrefix "unix://")-        <&> fromMaybe "/var/run/docker.sock"+  -- Ryuk needs access to the Docker daemon socket to reap resources. The way+  -- that socket is exposed depends on whether the daemon runs Linux or Windows+  -- containers. Docker automatically selects the matching ryuk image variant+  -- from the multi-arch manifest based on the daemon's OS.+  dockerSocketMount <- do+    onLinux <- isDockerOnLinux+    if onLinux+      then do+        dockerSocketLocation <-+          liftIO $+            lookupEnv "DOCKER_HOST"+              <&> (>>= stripPrefix "unix://")+              <&> fromMaybe "/var/run/docker.sock"+        pure (pack dockerSocketLocation, "/var/run/docker.sock")+      else+        -- Windows containers talk to the daemon through the named pipe rather+        -- than a unix socket. This is what the windows/amd64 ryuk build expects.+        pure ("\\\\.\\pipe\\docker_engine", "\\\\.\\pipe\\docker_engine")   ryukContainer <-     run $       containerRequest (fromTag ryukImageTag)@@ -682,9 +695,14 @@         -- Ryuk destroys itself once it reaped the resources,         -- no need to register itself with itself.         withoutReaper-        & setVolumeMounts [(pack dockerSocketLocation, "/var/run/docker.sock")]+        & setVolumeMounts [dockerSocketMount]         & setExpose [ryukPort]-        & setWaitingFor (waitUntilMappedPortReachable ryukPort)+        -- Wait for ryuk's own startup log rather than probing its port.+        -- A TCP probe (waitUntilMappedPortReachable) registers as a client+        -- that immediately disconnects without sending any labels; ryuk 0.14.0+        -- treats this as a completed session and exits with its 1 ns+        -- reconnection timeout, so the subsequent real connection gets RST.+        & setWaitingFor (waitForLogLine Stdout ("Started" `LazyText.isInfixOf`))         & setRm True    let (ryukContainerAddress, ryukContainerPort) =@@ -786,11 +804,20 @@ fromTag :: ImageTag -> ToImage fromTag tag = defaultToImage $ do   tracer <- askTracer-  output <- docker tracer ["pull", "--quiet", tag]-  return $-    Image-      { tag = strip (pack output)-      }+  pullWithRetry tracer 3+  where+    pull tracer = do+      output <- docker tracer ["pull", "--quiet", tag]+      pure $ Image {tag = strip (pack output)}+    pullWithRetry :: Tracer -> Int -> TestContainer Image+    pullWithRetry tracer 0 = pull tracer+    pullWithRetry tracer n = do+      result <- try (pull tracer)+      case result of+        Right image -> pure image+        Left (_ :: DockerException) -> do+          liftIO (threadDelay 2000000)+          pullWithRetry tracer (n - 1)  -- | Get an `Image` from an image id. This doesn't run @docker pull@ or any other Docker command -- on construction.@@ -1104,9 +1131,13 @@ waitUntilReady :: Container -> WaitUntilReady -> TestContainer () waitUntilReady container@Container {id} input = do   Config {configDefaultWaitTimeout} <- ask-  interpreter $ case configDefaultWaitTimeout of-    Just seconds -> waitUntilTimeout seconds input-    Nothing -> input+  interpreter $ case input of+    WaitUntilTimeout {} ->+      input+    _ ->+      case configDefaultWaitTimeout of+        Just seconds -> waitUntilTimeout seconds input+        Nothing -> input   where     interpreter :: WaitUntilReady -> TestContainer ()     interpreter wait =
src/TestContainers/Docker/Reaper.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}  module TestContainers.Docker.Reaper   ( Reaper (..),@@ -14,6 +15,8 @@   ) where +import Control.Concurrent (threadDelay)+import Control.Exception (IOException, try) import Control.Monad (replicateM) import Control.Monad.Trans.Resource (MonadResource, allocate) import Data.Text (Text, pack, unpack)@@ -58,7 +61,7 @@ -- @since 0.5.0.0 ryukImageTag :: Text ryukImageTag =-  "docker.io/testcontainers/ryuk:0.3.4"+  "docker.io/testcontainers/ryuk:0.14.0"  -- | Exposed port for the ryuk reaper. --@@ -84,24 +87,12 @@   (_releaseKey, (_socket, ryuk)) <-     allocate       ( do-          let hints =-                Socket.defaultHints {Socket.addrSocketType = Socket.Stream}-          address <--            head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))-          socket <--            Socket.socket-              (Socket.addrFamily address)-              (Socket.addrSocketType address)-              (Socket.addrProtocol address)-          Socket.connect socket (Socket.addrAddress address)--          -- Construct the reaper and regiter the session with it.-          -- Doing it here intead of in the teardown (like we did before)+          socket <- connectWithRetry 60+          -- Construct the reaper and register the session with it.+          -- Doing it here instead of in the teardown (like we did before)           -- guarantees the Reaper knows about our session.-          let reaper =-                newReaper sessionId (Ryuk socket)+          let reaper = newReaper sessionId (Ryuk socket)           register reaper sessionIdLabel sessionId-           pure (socket, reaper)       )       ( \(socket, _ryuk) -> do@@ -111,6 +102,29 @@       )    pure ryuk+  where+    -- On macOS (Docker Desktop / Colima), port-forwarding from the VM to the+    -- host can lag a moment behind the container's own "Started" log line.+    -- Retry the connect rather than failing immediately.+    connectWithRetry :: Int -> IO Socket.Socket+    connectWithRetry 0 = do+      let hints = Socket.defaultHints {Socket.addrSocketType = Socket.Stream}+      address <- head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))+      socket <- Socket.socket (Socket.addrFamily address) (Socket.addrSocketType address) (Socket.addrProtocol address)+      Socket.connect socket (Socket.addrAddress address)+      pure socket+    connectWithRetry attemptsLeft = do+      let hints = Socket.defaultHints {Socket.addrSocketType = Socket.Stream}+      address <- head <$> Socket.getAddrInfo (Just hints) (Just (unpack host)) (Just (show port))+      socket <- Socket.socket (Socket.addrFamily address) (Socket.addrSocketType address) (Socket.addrProtocol address)+      result <- try (Socket.connect socket (Socket.addrAddress address))+      case result of+        Right () ->+          pure socket+        Left (_ :: IOException) -> do+          Socket.close socket+          threadDelay 500000+          connectWithRetry (attemptsLeft - 1)  newReaper ::   -- | Session id
src/TestContainers/Image.hs view
@@ -23,21 +23,21 @@ -- | Image for Redis database. -- -- @--- redis = fromTag "redis:5.0"+-- redis = fromTag "redis:7.4" -- @ -- -- @since 0.1.0.0 redis :: ToImage redis =-  fromTag "redis:5.0"+  fromTag "redis:7.4"  -- | Image for Mongo database. -- -- @--- mongo = Tag "mongo:4.0.17"+-- mongo = Tag "mongo:7.0" -- @ -- -- @since 0.1.0.0 mongo :: ToImage mongo =-  fromTag "mongo:4.0.17"+  fromTag "mongo:7.0"
test/TestContainers/TastySpec.hs view
@@ -3,7 +3,7 @@  module TestContainers.TastySpec (main, test_all) where -import Data.Text.Lazy (isInfixOf)+import qualified Data.Text.Lazy as LazyText import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit import TestContainers.Tasty@@ -51,19 +51,17 @@    _rabbitmq <-     run $-      containerRequest (fromTag "rabbitmq:3.8.4")+      containerRequest (fromTag "rabbitmq:3.13")         & setRm False         & setExpose [5672]         & withNetwork net         & withFollowLogs consoleLogConsumer         & setWaitingFor-          ( waitForLogLine Stdout (("completed with" `isInfixOf`))-              <> waitUntilMappedPortReachable 5672-          )+          (waitUntilTimeout 300 (waitForLogLine Stdout ("started TCP listener" `LazyText.isInfixOf`)))    _nginx <-     run $-      containerRequest (fromTag "nginx:1.23.1-alpine")+      containerRequest (fromTag "nginx:1.27-alpine")         & setExpose [80]         & withNetwork net         & setWaitingFor@@ -73,7 +71,7 @@    _jaeger <-     run $-      containerRequest (fromTag "jaegertracing/all-in-one:1.6")+      containerRequest (fromTag "jaegertracing/all-in-one:1.62.0")         & setExpose ["5775/udp", "6831/udp", "6832/udp", "5778", "16686/tcp"]         & withNetwork net         & setWaitingFor@@ -81,7 +79,7 @@    _postgres <-     run $-      containerRequest (fromTag "postgres:16-alpine")+      containerRequest (fromTag "postgres:17-alpine")         & withCopyFileToContainer "test/data/init-script.sql" "/docker-entrypoint-initdb.d/"    _helloWorld <-
testcontainers.cabal view
@@ -1,6 +1,6 @@ cabal-version:      >=1.10 name:               testcontainers-version:            0.5.3.0+version:            0.5.4.0 synopsis:           Docker containers for your integration tests. description:   testcontainers is a Haskell library that provides a friendly API to@@ -12,7 +12,7 @@ license-file:       LICENSE author:             Alex Biehl maintainer:         alex.biehl@gmail.com-copyright:          2023 Alex Biehl+copyright:          2026 Alex Biehl category:           Development build-type:         Simple extra-source-files:@@ -21,7 +21,7 @@   test/data/init-script.sql  tested-with:-  GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.4 || ==9.4.2 || ==9.8.2+  GHC ==9.2 || ==9.12  source-repository head   type:     git@@ -86,7 +86,7 @@     , hspec           >=2.0   && <3.0     , tasty     , tasty-discover  >=4.2.1 && <6-    , tasty-hspec+    , tasty-hspec     >=1.1.3 && <1.3     , tasty-hunit     , testcontainers     , text