testcontainers / testcontainers-go

Testcontainers for Go is a Go package that makes it simple to create and clean up container-based dependencies for automated integration/smoke tests. The clean, easy-to-use API enables developers to programmatically define containers that should be run as part of a test and clean up those resources when the test is done.
https://golang.testcontainers.org
MIT License
3.49k stars 477 forks source link

[Bug]: Issue with podman on windows #2769

Open jorgepsmatos opened 1 week ago

jorgepsmatos commented 1 week ago

Testcontainers version

0.33.0

Using the latest Testcontainers version?

Yes

Host OS

Windows 10

Host arch

x86

Go version

1.23

Docker version

Client:       Podman Engine
Version:      5.2.0
API Version:  5.2.0
Go Version:   go1.22.5
Git Commit:   b22d5c61eef93475413724f49fd6a32980d2c746
Built:        Fri Aug  2 13:10:51 2024
OS/Arch:      windows/amd64

Server:       Podman Engine
Version:      5.2.2
API Version:  5.2.2
Go Version:   go1.22.6
Built:        Wed Aug 21 01:00:00 2024
OS/Arch:      linux/amd64

Docker info

host:
  arch: amd64
  buildahVersion: 1.37.2
  cgroupControllers:
  - cpuset
  - cpu
  - cpuacct
  - blkio
  - memory
  - devices
  - freezer
  - net_cls
  - perf_event
  - net_prio
  - hugetlb
  - pids
  - rdma
  - misc
  cgroupManager: cgroupfs
  cgroupVersion: v1
  conmon:
    package: conmon-2.1.10-1.fc40.x86_64
    path: /usr/bin/conmon
    version: 'conmon version 2.1.10, commit: '
  cpuUtilization:
    idlePercent: 99.8
    systemPercent: 0.11
    userPercent: 0.08
  cpus: 8
  databaseBackend: sqlite
  distribution:
    distribution: fedora
    variant: container
    version: "40"
  eventLogger: journald
  freeLocks: 2033
  hostname: TRU-D290Q13
  idMappings:
    gidmap: null
    uidmap: null
  kernel: 5.15.153.1-microsoft-standard-WSL2
  linkmode: dynamic
  logDriver: journald
  memFree: 15896633344
  memTotal: 16674488320
  networkBackend: netavark
  networkBackendInfo:
    backend: netavark
    dns:
      package: aardvark-dns-1.12.1-1.fc40.x86_64
      path: /usr/libexec/podman/aardvark-dns
      version: aardvark-dns 1.12.1
    package: netavark-1.12.1-1.fc40.x86_64
    path: /usr/libexec/podman/netavark
    version: netavark 1.12.1
  ociRuntime:
    name: crun
    package: crun-1.15-1.fc40.x86_64
    path: /usr/bin/crun
    version: |-
      crun version 1.15
      commit: e6eacaf4034e84185fd8780ac9262bbf57082278
      rundir: /run/crun
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +LIBKRUN +WASM:wasmedge +YAJL
  os: linux
  pasta:
    executable: /usr/bin/pasta
    package: passt-0^20240821.g1d6142f-1.fc40.x86_64
    version: |
      pasta 0^20240821.g1d6142f-1.fc40.x86_64
      Copyright Red Hat
      GNU General Public License, version 2 or later
        <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
      This is free software: you are free to change and redistribute it.
      There is NO WARRANTY, to the extent permitted by law.
  remoteSocket:
    exists: true
    path: /run/podman/podman.sock
  rootlessNetworkCmd: pasta
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: false
    seccompEnabled: true
    seccompProfilePath: /usr/share/containers/seccomp.json
    selinuxEnabled: false
  serviceIsRemote: true
  slirp4netns:
    executable: ""
    package: ""
    version: ""
  swapFree: 4294967296
  swapTotal: 4294967296
  uptime: 1h 16m 17.00s (Approximately 0.04 days)
  variant: ""
plugins:
  authorization: null
  log:
  - k8s-file
  - none
  - passthrough
  - journald
  network:
  - bridge
  - macvlan
  - ipvlan
  volume:
  - local
registries:
  search:
  - docker.io
store:
  configFile: /usr/share/containers/storage.conf
  containerStore:
    number: 2
    paused: 0
    running: 1
    stopped: 1
  graphDriverName: overlay
  graphOptions:
    overlay.imagestore: /usr/lib/containers/storage
    overlay.mountopt: nodev,metacopy=on
  graphRoot: /var/lib/containers/storage
  graphRootAllocated: 1081101176832
  graphRootUsed: 3346808832
  graphStatus:
    Backing Filesystem: extfs
    Native Overlay Diff: "false"
    Supports d_type: "true"
    Supports shifting: "false"
    Supports volatile: "true"
    Using metacopy: "true"
  imageCopyTmpDir: /var/tmp
  imageStore:
    number: 12
  runRoot: /run/containers/storage
  transientStore: false
  volumePath: /var/lib/containers/storage/volumes
version:
  APIVersion: 5.2.2
  Built: 1724198400
  BuiltTime: Wed Aug 21 01:00:00 2024
  GitCommit: ""
  GoVersion: go1.22.6
  Os: linux
  OsArch: linux/amd64
  Version: 5.2.2

What happened?

I get the following error when trying to run on Windows:

create container: container create: Error response from daemon: container create: unable to find network with name or ID bridge: network not found: creating reaper failed

With TESTCONTAINERS_RYUK_DISABLE=true this does not happen, but I would like to get it working with ryuk too.

According to this issue on the podman repo https://github.com/containers/podman/issues/14983, the api responds that the "bridge" network exists even tough it doesn't, so I would assume it would be enough to trick testcontainers into finding it

I used this python script to test the API through the socket and it does respond succesfully stating the bridge network exists so I assume the issue is really on testcontainers side

# needs pip install pywin32
import win32pipe, win32file, pywintypes

pipe_name = r'\\.\pipe\docker_engine'

try:
    # Connect to the named pipe
    handle = win32file.CreateFile(
        pipe_name,
        win32file.GENERIC_READ | win32file.GENERIC_WRITE,
        0, None,
        win32file.OPEN_EXISTING,
        0, None
    )

    # Send an HTTP request to Docker Engine
    request = "GET /networks/bridge HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"
    win32file.WriteFile(handle, request.encode('utf-8'))

    # Read the response
    result, response = win32file.ReadFile(handle, 4096)
    print("Response from Docker Engine:\n", response.decode('utf-8'))

except pywintypes.error as e:
    print(f"Failed to connect to the named pipe: {e}")

finally:
    # Close the pipe handle
    if 'handle' in locals():
        win32file.CloseHandle(handle)

Response:

Response from Docker Engine:
 HTTP/1.1 200 OK
Api-Version: 1.41
Content-Type: application/json
Libpod-Api-Version: 5.2.2
Server: Libpod/5.2.2 (linux)
X-Reference-Id: 0xc0006828e0
Date: Thu, 05 Sep 2024 10:30:58 GMT
Content-Length: 633
Connection: close

{"Name":"bridge","Id":"2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9","Created":"2024-09-05T11:30:58.393239248+01:00","Scope":"local","Driver":"bridge","EnableIPv6":false,"IPAM":{"Driver":"default","Options":{"driver":"host-local"},"Config":[{"Subnet":"10.88.0.0/16","Gateway":"10.88.0.1"}]},"Internal":false,"Attachable":false,"Ingress":false,"ConfigFrom":{"Network":""},"ConfigOnly":false,"Containers":{"2e340a7a0c24f2cd1c051055af30e736b788716b203b124634a500e4146dd3f4":{"Name":"my-nginx","EndpointID":"","MacAddress":"5e:00:61:e1:23:74","IPv4Address":"10.88.0.3/16","IPv6Address":""}},"Options":{},"Labels":{}}

Relevant log output

create container: container create: Error response from daemon: container create: unable to find network with name or ID bridge: network not found: creating reaper failed

Additional information

code sample:

    req := testcontainers.ContainerRequest{
        Image: "postgres:12",
        Env: map[string]string{
            "POSTGRES_USER":     dbUser,
            "POSTGRES_PASSWORD": dbPassword,
            "POSTGRES_DB":       dbName,
        },
        ExposedPorts: []string{"5432/tcp"},
        Cmd:          []string{"postgres", "-c", "fsync=off"},
        WaitingFor:   wait.ForLog("database system is ready to accept connections").WithStartupTimeout(5 * time.Second),
    }

    genericContainerReq := testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
    }

    postgresContainer, err := testcontainers.GenericContainer(ctx, genericContainerReq)
mdelapenya commented 1 week ago

Hi @jorgepsmatos the current DockerProvider struct can be created for Podman: https://github.com/testcontainers/testcontainers-go/blob/main/provider.go#L119-L125

Can you verify if setting the provider in the GenericContainerRequest helps you here?

jorgepsmatos commented 1 week ago

With the ProviderType like this:

    req := testcontainers.ContainerRequest{
        Image: "postgres:12",
        Env: map[string]string{
            "POSTGRES_USER":     dbUser,
            "POSTGRES_PASSWORD": dbPassword,
            "POSTGRES_DB":       dbName,
        },
        ExposedPorts: []string{"5432/tcp"},
        Cmd:          []string{"postgres", "-c", "fsync=off"},
        WaitingFor:   wait.ForLog("database system is ready to accept connections").WithStartupTimeout(5 * time.Second),
    }

    genericContainerReq := testcontainers.GenericContainerRequest{
        ContainerRequest: req,
        Started:          true,
        ProviderType:     testcontainers.ProviderPodman,
    }

    postgresContainer, err := testcontainers.GenericContainer(ctx, genericContainerReq)

I get another error:

create container: started hook: wait until ready: get state: Error response from daemon: no container with name or ID "b58a314d3c65bf52772c406ea584299bd9689048e61ac6b10268fbbf3ecce182" found: no such container: could not start container: creating reaper failed

Besides, since this code is supposed to be shared by a team and has to run in a GitLab pipeline without podman I would prefer to keep it generic

mdelapenya commented 1 week ago

You probably have read https://golang.testcontainers.org/system_requirements/using_podman/, so I'm not sure what's going on (I do not have a Windows machine at hand).

FYI we are planning to remove the Provider abstraction in an eventual v1 (see https://github.com/testcontainers/testcontainers-go/tree/v1), so we expect that the container runtime should be located simply by the existing discovery mechanism: https://golang.testcontainers.org/features/configuration/#docker-host-detection.

jorgepsmatos commented 1 week ago

I'll keep investigating when I have time and share my findings here.

Regarding the second point, that is acceptable for us, we're already setting DOCKER_HOST on our CI, and locally each dev should do it too