testcontainers / testcontainers-dotnet

A library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions.
https://dotnet.testcontainers.org
MIT License
3.78k stars 274 forks source link

[Bug]: If you want a remote host from windows it will fail with a null reference exception #929

Closed paule96 closed 1 year ago

paule96 commented 1 year ago

Testcontainers version

3.2.0

Using the latest Testcontainers version?

Yes

Host OS

Windows

Host arch

x86

.NET version

6.0.304

Docker version

Client: Docker Engine - Community
 Version:           24.0.2
 API version:       1.43
 Go version:        go1.20.4
 Git commit:        cb74dfc
 Built:             Thu May 25 21:55:21 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          24.0.2
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.4
  Git commit:       659604f
  Built:            Thu May 25 21:54:24 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.21
  GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
 runc:
  Version:          1.1.7
  GitCommit:        v1.1.7-0-g860f061
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker info

Client: Docker Engine - Community
 Version:    24.0.2
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.10.5
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.18.1
    Path:     /usr/libexec/docker/cli-plugins/docker-compose
  scan: Docker Scan (Docker Inc.)
    Version:  v0.23.0
    Path:     /usr/libexec/docker/cli-plugins/docker-scan

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 6
 Server Version: 24.0.2
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 3dce8eb055cbb6872793272b4f20ed16117344f8
 runc version: v1.1.7-0-g860f061
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
 Kernel Version: 3.10.0-1160.76.1.el7.x86_64
 Operating System: CentOS Linux 7 (Core)
 OSType: linux
 Architecture: x86_64
 CPUs: 2
 Total Memory: 3.682GiB
 Name: xxxxxxx1dg
 ID: DZIF:CF7R:OXVQ:N4GH:2UFB:RGTG:VTCS:NMUU:5LCO:RJEV:EJSY:JC67
 Docker Root Dir: /mnt/builddrive/docker
 Debug Mode: false
 HTTP Proxy: http://proxy.xxxxx.xxxx:8080
 HTTPS Proxy: http://proxy.xxxx.xxx:8080
 No Proxy: xxxx.xxx,xxxx.xxxxxx.xxxx,xxxxx.xxxxxx,xxxxxx.xxxx,0.0.0.0,localhost,127.0.0.1
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://common-docker.artifacts.xxxx.xxx/
 Live Restore Enabled: false

What happened?

If you run your testcontainers test on your windows development machine, but want to run the containers on a remote host, it is required that you have docker desktop installed and running. if you disable / uninstall it testcontainers will fail with a null reference exception. The important thing is: This happens after upgrading our solution from testcontainers 2.3.0 to testcontainers 3.2.0.

our custom IDockerEndpointAuthenticationConfiguration looks like that:

  public class OurDockerAuth : IDockerEndpointAuthenticationConfiguration
  {
      public OurDockerAuth(Uri Endpoint, string pfxFile, string passwort)
      {
          this.Endpoint = Endpoint;
          var cert = new X509Certificate2(pfxFile, passwort);
          var creds = new CertificateCredentials(cert);
          // This is done for the CI pipeline, because the CI server don't trust or CA
          creds.ServerCertificateValidationCallback += (o, c, ch, er) => true;
          this.Credentials = creds;
      }
      public Uri Endpoint { get; }
      public Credentials Credentials { get; }

      public DockerClientConfiguration GetDockerClientConfiguration(Guid sessionId = default)
      {
          return new DockerClientConfiguration(Endpoint, Credentials);
      }
  }

the error message that you got:

UnixSocketMount.GetSocketPath()
UnixSocketMount.get_Source()
<>c.<Convert>b__1_0(IMount mount)
SelectArrayIterator`2.ToList()
ContainerConfigurationConverter.ctor(IContainerConfiguration configuration)
DockerContainerOperations.RunAsync(IContainerConfiguration configuration, CancellationToken ct)
TestcontainersClient.RunAsync(IContainerConfiguration configuration, CancellationToken ct)
DockerContainer.UnsafeCreateAsync(CancellationToken ct)
DockerContainer.StartAsync(CancellationToken ct)
ResourceReaper.GetAndStartNewAsync(Guid sessionId, IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConfig, IImage resourceReaperImage, IMount dockerSocket, Boolean requiresPrivilegedMode, TimeSpan initTimeout, CancellationToken ct)
ResourceReaper.GetAndStartNewAsync(Guid sessionId, IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConfig, IImage resourceReaperImage, IMount dockerSocket, Boolean requiresPrivilegedMode, TimeSpan initTimeout, CancellationToken ct)
ResourceReaper.GetAndStartDefaultAsync(IDockerEndpointAuthenticationConfiguration dockerEndpointAuthConfig, Boolean isWindowsEngineEnabled, CancellationToken ct)
TestcontainersClient.RunAsync(IContainerConfiguration configuration, CancellationToken ct)
DockerContainer.UnsafeCreateAsync(CancellationToken ct)
DockerContainer.StartAsync(CancellationToken ct)
DockerDbBuilder.RunDockerCommands(Func`1 command) line 241

from my research so far, I'm pretty sure that the method GetVersionAsync in DockerSystemOperations works wrong. It just detects the operation system by checking from the running test environment the OS. (it says windows for me, but my docker host is linux) Then the result in this method will be used to detect if the docker command will be executed against docker desktop or not in RunAsync from the class TestcontainersClient. this will call then GetAndStartDefaultAsync from the resource reaper that will fail.

Relevant log output

No response

Additional information

No response

HofmeisterAn commented 1 year ago

It looks like those lines are causing the bug:

https://github.com/testcontainers/testcontainers-dotnet/blob/f867967acf93207bfa1db3dd3843d466e65b1847/src/Testcontainers/Configurations/TestcontainersSettings.cs#L175-L177

in your environment DockerEndpointAuthConfig is likely null (and you are using WithDockerEndpoint(...)) then getting the schema from the endpoint will fail here:

https://github.com/testcontainers/testcontainers-dotnet/blob/f867967acf93207bfa1db3dd3843d466e65b1847/src/Testcontainers/Containers/ResourceReaper.cs#L456-L457

I guess if you set the environment variable DOCKER_HOST to a fake value like tcp://127.0.0.1:2375 before running the tests, it will work fine (workaround).

HofmeisterAn commented 1 year ago

I am able to reproduce the issue. I can fix it later in the day or tomorrow. Thanks for creating the issue.