When attempting to execute a command on a PostgresqlContainer, a System.MissingMethodException is thrown
LanceFHadBiomerieux opened this issue · 3 comments
When using this code
var builder = new ContainerBuilder<PostgreSqlContainer>() .ConfigureDatabaseConfiguration(_userId, _password, dbName) .ConfigureContainer((context, container) => { container.BindMounts.Add(new Bind { AccessMode = AccessMode.ReadWrite, ContainerPath = "/mnt", HostPath = testDataDir }); }); _container = builder.Build(); await _container.StartAsync(); _connectionString = _container.GetConnectionString();
To create a PostgresqlContainer for Unit testing, I want to execute this command inside the container to restore from a mounted dump file.
var commands = new List<string> { "./bin/bash", "pg_restore", // "-c", "-d", dbName, "-U", _userId, $"mnt/{dumpFile}" }; var result = _container.ExecuteCommand(commands.ToArray()).ConfigureAwait(true);
This follows the syntax outlined in the integration tests for this library. However, when I do so, it throws this exception
System.MissingMethodException : Method not found: System.Threading.Tasks.Task1<Docker.DotNet.Models.ContainerExecCreateResponse> Docker.DotNet.IContainerOperations.ExecCreateContainerAsync(System.String, Docker.DotNet.Models.ContainerExecCreateParameters, System.Threading.CancellationToken).
at TestContainers.Container.Abstractions.AbstractContainer.ExecuteCommand(String[] command)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at TestContainers.Container.Abstractions.AbstractContainer.ExecuteCommand(String[] command)
While digging around to try and figure out what the problem was, I came across what I believe is the issue. The ExecCreateContainerAsync(string, params, ct) method does not reside in the Containers object of the DockerClient. If you search in this file (the ContainerOperations file in Docket.DotNet)
https://github.com/dotnet/Docker.DotNet/blob/386bfd6d84e94205e75102cd838147683d42d89c/src/Docker.DotNet/Endpoints/ContainerOperations.cs
You will not find a method with that signature.
However, the ExecuteCommand function defined here
https://github.com/isen-ng/testcontainers-dotnet/blob/68a6e0c43f8d9ddab37a085138c240537f228568/src/Container.Abstractions/AbstractContainer.cs
attempts to call ExecCreateContainerAsync from DockerClient.Containers.
The ExecCreateContainerAsync(string, params, ct) method is actually found in the Exec object inside the DocketClient, which you can find defined here
https://github.com/dotnet/Docker.DotNet/blob/f58748616cc5b679b25496926c5688294c94d850/src/Docker.DotNet/Endpoints/ExecOperations.cs
In this file you can find the method with the signature that ExecuteCommand is looking for, and in this next file you can see where the DockerClient has both a ContainerOperations and an ExecOperations object defined in it. I really need to be able to execute commands inside my postgres container and would like for this issue to be fixed. Thanks
This is because of a version mismatch of the Docker.DotNet
library.
If the ExecCreateContainerAsync
operation isn't in the ContainerOperations
object, the code would not even compile, and I would not even be able to make a release.
In this library, we use Docker.DotNet
version 3.152.2. The ExecCreateContainerAsync
still exists in this class.
In 3.152.4, there is a breaking change. The ExecCreateContainerAsync
no longer exists in this class.
My suspicion is that somewhere in your project, the version for Docker.DotNet
has been overridden.
Closing because of inactivity. If there are new problems, please open a new ticket
I ended up using a workaround because I couldn't get the mismatch of Docker.DotNet versions resolved in a meaningful time. This is how I ended up doing it. In case anyone in future has the same problem and needs a workaround.
public class TestDatabase
{
private PostgreSqlContainer _container;
private string _userId = "";
private string _password = "";
private string dbName = "";
public string _connectionString { get; set; }
public TestDatabase()
{
}
public async Task startContainer()
{
var testDataDir = $"{AppDomain.CurrentDomain.BaseDirectory}TestData".Replace("\\", "/");
var builder = new ContainerBuilder<PostgreSqlContainer>()
.ConfigureDatabaseConfiguration(_userId, _password, dbName)
.ConfigureContainer((context, container) =>
{
container.BindMounts.Add(new Bind
{
AccessMode = AccessMode.ReadWrite,
ContainerPath = "/mnt",
HostPath = testDataDir
});
});
_container = builder.Build();
await _container.StartAsync();
_connectionString = _container.GetConnectionString();
}
public async Task ExecuteRestore(string dumpFile)
{
var dockerClient = new DockerClientConfiguration().CreateClient();
var exec = await dockerClient.Exec.ExecCreateContainerAsync(_container.ContainerId,
new ContainerExecCreateParameters
{
AttachStderr = true,
AttachStdout = true,
AttachStdin = true,
Cmd = new List<string>
{
"pg_restore",
"-d",
dbName,
"-U",
_userId,
$"mnt/{dumpFile}"
}
});
var results = await dockerClient.Exec.StartAndAttachContainerExecAsync(exec.ID, false);
await results.CopyOutputToAsync(null, Console.OpenStandardOutput(), Console.OpenStandardError(), CancellationToken.None);
}
}