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.67k stars 254 forks source link

[Bug]: Docker image creation fails for ASP.NET Core 8 Web API project #1078

Closed Crumbler closed 7 months ago

Crumbler commented 7 months ago

Testcontainers version

3.6.0

Using the latest Testcontainers version?

Yes

Host OS

Windows 10 Pro 19045.3803

Host arch

Intel x64

.NET version

8.0.100

Docker version

Client:
 Cloud integration: v1.0.35+desktop.5
 Version:           24.0.7
 API version:       1.43
 Go version:        go1.20.10
 Git commit:        afdd53b
 Built:             Thu Oct 26 09:08:44 2023
 OS/Arch:           windows/amd64
 Context:           default

Server: Docker Desktop 4.26.1 (131620)
 Engine:
  Version:          24.0.7
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.10
  Git commit:       311b9ff
  Built:            Thu Oct 26 09:08:02 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.25
  GitCommit:        d8f198a4ed8892c764191ef7b3b06d8a2eeb5c7f
 runc:
  Version:          1.1.10
  GitCommit:        v1.1.10-0-g18a0cb0
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Docker info

Client:
 Version:    24.0.7
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.12.0-desktop.2
    Path:     C:\Program Files\Docker\cli-plugins\docker-buildx.exe
  compose: Docker Compose (Docker Inc.)
    Version:  v2.23.3-desktop.2
    Path:     C:\Program Files\Docker\cli-plugins\docker-compose.exe
  dev: Docker Dev Environments (Docker Inc.)
    Version:  v0.1.0
    Path:     C:\Program Files\Docker\cli-plugins\docker-dev.exe
  extension: Manages Docker extensions (Docker Inc.)
    Version:  v0.2.21
    Path:     C:\Program Files\Docker\cli-plugins\docker-extension.exe
  feedback: Provide feedback, right in your terminal! (Docker Inc.)
    Version:  0.1
    Path:     C:\Program Files\Docker\cli-plugins\docker-feedback.exe
  init: Creates Docker-related starter files for your project (Docker Inc.)
    Version:  v0.1.0-beta.10
    Path:     C:\Program Files\Docker\cli-plugins\docker-init.exe
  sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.)
    Version:  0.6.0
    Path:     C:\Program Files\Docker\cli-plugins\docker-sbom.exe
  scan: Docker Scan (Docker Inc.)
    Version:  v0.26.0
    Path:     C:\Program Files\Docker\cli-plugins\docker-scan.exe
  scout: Docker Scout (Docker Inc.)
    Version:  v1.2.0
    Path:     C:\Program Files\Docker\cli-plugins\docker-scout.exe

Server:
 Containers: 1
  Running: 0
  Paused: 0
  Stopped: 1
 Images: 9
 Server Version: 24.0.7
 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: d8f198a4ed8892c764191ef7b3b06d8a2eeb5c7f
 runc version: v1.1.10-0-g18a0cb0
 init version: de40ad0
 Security Options:
  seccomp
   Profile: unconfined
 Kernel Version: 5.15.133.1-microsoft-standard-WSL2
 Operating System: Docker Desktop
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 7.733GiB
 Name: docker-desktop
 ID: a2a88f6a-5f21-432f-b153-0252e0c04c27
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 HTTP Proxy: http.docker.internal:3128
 HTTPS Proxy: http.docker.internal:3128
 No Proxy: hubproxy.docker.internal
 Experimental: false
 Insecure Registries:
  hubproxy.docker.internal:5555
  127.0.0.0/8
 Live Restore Enabled: false

WARNING: No blkio throttle.read_bps_device support
WARNING: No blkio throttle.write_bps_device support
WARNING: No blkio throttle.read_iops_device support
WARNING: No blkio throttle.write_iops_device support
WARNING: daemon is not using the default seccomp profile

What happened?

I have an ASP.NET Core 8 Web API project with a dockerfile in it. Building an image using ImageFromDockerfileBuilder fails due to being unable to copy the project file (see log below). The dockerfile is generated automatically by Visual Studio 2022.

Building the docker image using the Docker CLI works just fine, and so does starting the container via Visual Studio using the Docker profile from launchSettings.json.

Steps to reproduce this behavior (using Visual Studio 2022):

  1. Create a new ASP.NET Core Web API project. Do not use https, OpenAPI, controllers or any authentication. Enable Docker with the Docker OS set to Linux.
  2. Create a new .NET Console App in the same solution. Add a reference to the OpenContainers NuGet package.
  3. Use the code provided below in Program.cs and run the project.
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Configurations;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
using Microsoft.Extensions.Logging;

namespace Tester
{
    public static class Program
    {
        private static IFutureDockerImage? serviceImage;
        private static IContainer? serviceContainer;

        public static async Task Main()
        {
            Console.WriteLine("Hello, World!");

            var factory = LoggerFactory.Create(o => o.AddConsole());
            TestcontainersSettings.Logger = factory.CreateLogger("logger");

            serviceImage = new ImageFromDockerfileBuilder()
                .WithName("containertest")
                .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), "ContainerTest")
                .WithDockerfile("Dockerfile")
                .Build();

            await serviceImage.CreateAsync();

            serviceContainer = new ContainerBuilder()
                .WithCleanUp(true)
                .WithEnvironment("ASPNETCORE_HTTP_PORTS", "8080")
                .WithImage(serviceImage)
                .WithPortBinding(8080, true)
                .Build();

            ushort port = serviceContainer.GetMappedPublicPort(8080);
            Console.WriteLine($"Port: {port}");

            await serviceContainer.StartAsync();

            Console.ReadLine();

            await serviceContainer.DisposeAsync();
            await serviceImage.DisposeAsync();
        }
    }
}

Relevant log output

info: logger[0]
      Connected to Docker:
        Host: npipe://./pipe/docker_engine
        Server Version: 24.0.7
        Kernel Version: 5.15.133.1-microsoft-standard-WSL2
        API Version: 1.43
        Operating System: Docker Desktop
        Total Memory: 7.73 GB
info: logger[0]
      Pattern ^(.+)\/\.idea added to the regex cache
info: logger[0]
      Pattern ^([\\\/]?(\.idea\b|$)) added to the regex cache
info: logger[0]
      Pattern ^([\\\/]?((.+)\/\.vs\b|$)) added to the regex cache
info: logger[0]
      Pattern ^([\\\/]?(\.vs\b|$)) added to the regex cache
info: logger[0]
      Pattern ^([\\\/]?(\.dockerignore\b|$)) added to the regex cache
info: logger[0]
      Pattern ^([\\\/]?(Dockerfile\b|$)) added to the regex cache
fail: logger[0]
      COPY failed: file not found in build context or excluded by .dockerignore: stat ContainerTest/ContainerTest.csproj: file does not exist
Unhandled exception. System.InvalidOperationException: Docker image containertest:latest has not been created.
   at DotNet.Testcontainers.Clients.DockerImageOperations.BuildAsync(IImageFromDockerfileConfiguration configuration, ITarArchive dockerfileArchive, CancellationToken ct)
   at DotNet.Testcontainers.Clients.TestcontainersClient.BuildAsync(IImageFromDockerfileConfiguration configuration, CancellationToken ct)
   at DotNet.Testcontainers.Images.FutureDockerImage.UnsafeCreateAsync(CancellationToken ct)
   at DotNet.Testcontainers.Images.FutureDockerImage.CreateAsync(CancellationToken ct)
   at Tester.Program.Main() in F:\My projects\ContainerTest\Tester\Program.cs:line 27
   at Tester.Program.<Main>()

Additional information

Sample project: https://github.com/Crumbler/ContainerTest

HofmeisterAn commented 7 months ago

I think you are using incorrect paths. The build context, respectively the paths inside the Dockerfile, look wrong. The log message says:

COPY failed: file not found in build context or excluded by .dockerignore: stat ContainerTest/ContainerTest.csproj: file does not exist

Which makes sense, because your build context (root directory being F:\My projects\ContainerTest\ContainerTest\) does not contain ContainerTest\ContainerTest.csproj. The correct copy instruction path is ContainerTest.csproj.

Move the Dockerfile to your solution file directory and set the Dockerfile root directory to WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty) instead.

Crumbler commented 7 months ago

@HofmeisterAn Huge thanks to you. I've edited the Dockerfile and now everything works as intended. I've realized that Visual Studio uses the Solution folder as the build context by default.

HofmeisterAn commented 6 months ago

👍 I have seen similar issues with VS's generated configuration before. I definitely need to take a look into the configuration VS generates. Maybe we can support it out of the box in Testcontainers.