dotnet / Docker.DotNet

:whale: .NET (C#) Client Library for Docker API
https://www.nuget.org/packages/Docker.DotNet/
MIT License
2.26k stars 380 forks source link

How to send commands to a container after creating and running it #617

Closed IAmTheBusiness closed 1 year ago

IAmTheBusiness commented 1 year ago

I created a container like so:

Dictionary<string, IList<PortBinding>> portBindings = new Dictionary<string, IList<PortBinding>>();
        PortBinding portBinding = new PortBinding();
        portBinding.HostPort = "3001";
        portBinding.HostIP = null;
        List<PortBinding> pList = new List<PortBinding>();
        pList.Add(portBinding);
        portBindings.Add("3001/tcp", pList);
        var response = await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters()
        {
            Image = "images/myemulator:version1",
            HostConfig = new HostConfig()
            {
                PortBindings = portBindings,
                Binds = new[] { @"C:\My\emulator:/data" }
            },
            Name = "MyEmulator",
            Platform = "linux/i386",
            Tty = true
        });

Then I started it:

await _dockerClient.Containers.StartContainerAsync(response.ID, new ContainerStartParameters());

Now, I want to execute two commands:

cd data

and

./myemulator_1200 -E1 -G1 -H1 -FTestScript.txt

How would I go about executing those commands?

HofmeisterAn commented 1 year ago

You need to create the command with IExecOperations.ExecCreateContainerAsync and read the response with IExecOperations.InspectContainerExecAsync, here is an example.

IAmTheBusiness commented 1 year ago

Thank you. I now have an error. Here is my code and the error:

List<string> commands = new List<string>();
        commands.Add("cd data");
        commands.Add("./myemulator_1200 -E1 -G1 -H1 -FTestScript.txt");

        var execCreateParameters = new ContainerExecCreateParameters
        {
            Cmd = commands,
            AttachStdout = true,
            AttachStderr = true,
        };

        var execCreateResponse = await _dockerClient.Exec.ExecCreateContainerAsync(response.ID, execCreateParameters, default(CancellationToken));

        using (var stdOutAndErrStream = await _dockerClient.Exec.StartAndAttachContainerExecAsync(execCreateResponse.ID, false, default(CancellationToken)))
        {
            var (stdout, stderr) = await stdOutAndErrStream.ReadOutputToEndAsync(default(CancellationToken));

            var execInspectResponse = await _dockerClient.Exec.InspectContainerExecAsync(execCreateResponse.ID, default(CancellationToken));

            Console.WriteLine($"Standard Out: {stdout}");
            Console.WriteLine($"Standard Error: {stderr}");
            Console.WriteLine($"Exit Code: {execInspectResponse.ExitCode}");
            Console.Read();
        }

I get the following response:

Standard Out: OCI runtime exec failed: exec failed: unable to start container process: exec: "cd data": executable file not found in $PATH: unknown Standard Error: Exit Code: 126

HofmeisterAn commented 1 year ago

You are using the API wrong. It receives a single command, where each list item is an argument. You need to send two commands, they look something like:

command1.Add("cd");
command1.Add("data");

// Either the binary is in the PATH env variable or you need the absolute path.
command2.Add("myemulator_1200");
command2.Add("-E1");
command2.Add("-G1");
command2.Add("-H1");
command2.Add("-FTestScript.txt");

!NOTE!: I do not think changing the directory to the binary will work. You probably need the absolute path, like command2.Add("/data/myemulator_1200");.

IAmTheBusiness commented 1 year ago

What I ended up doing that worked:

List<string> commands = new List<string>();       
commands.Add("/data/hid_aero_emulatorx32_1_29_1_0633");
commands.Add("-E1");
commands.Add("-G1");
commands.Add("-H1");
commands.Add("-F/data/TestScript.txt");

No cd data bits at all.

Thank you for your help. Closing this now.