2

I want to execute a powershell command in a docker container running on a windows host.

The specific command I want to execute is "powershell Get-PSDrive C | Select-Object Used,Free"

I have implemented this using the Docker API for python and it is simple like calling:

cmd = "powershell Get-PSDrive C | Select-Object Used,Free"
output = container.exec_run(cmd)

This works as intended, but I need to implement this in golang.

But somehow, it is not clear for me how to interact with the Docker API for golang. I looked into the API and was confused by the hijackedSession. How do I have to setup the calls for ContainerExecCreate, ContainerExecAttach and ContainerExecStart ?

I expect the golang script to deliver the same results like the python code does:

        Used         Free
        ----         ----
199181606912 307151622144

Which then can be parsed by me.

1
  • Just a quick info for users trying to interface windows containers using powershell. The nanoserver docker image does not include powershell. I used servercore. Commented Apr 22, 2019 at 9:11

1 Answer 1

1
+50

The HijackedResponse struct:

type HijackedResponse struct {
    Conn   net.Conn
    Reader *bufio.Reader
}


You need to copy the response from the resp.Reader,here is my code:

package main

import (
    "bytes"
    "context"
    "fmt"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
    "github.com/docker/docker/pkg/stdcopy"
    "strings"
)

func readFromCommand() (string, error) {
    cli, err := client.NewEnvClient()
    if err != nil {
        return "", err
    }
    ctx := context.Background()
    config := types.ExecConfig{
        Cmd:          strings.Split("powershell Get-PSDrive C | Select-Object Used,Free", " "),
        AttachStdout: true,
        AttachStderr: true,
    }
    response, err := cli.ContainerExecCreate(ctx,
        // container id
        "cf59d65ab1", config)
    if err != nil {
        return "", err
    }
    execID := response.ID

    resp, err := cli.ContainerExecAttach(ctx, execID, config)
    if err != nil {
        return "", err
    }
    defer resp.Close()
    stdout := new(bytes.Buffer)
    stderr := new(bytes.Buffer)
    _, err = stdcopy.StdCopy(stdout, stderr, resp.Reader)
    if err != nil {
        return "", err
    }
    s := stdout.String()
    fmt.Println(s)
    i := stderr.String()
    fmt.Println(i)
    return s, nil

}

Do remember to change the container id.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your example. But, is there something missing? func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecStartCheck) (types.HijackedResponse, error) says that the configuration is of type ExecStartCheck. In the above example a config of type ExecConfig is passed.
@semaph0r In my docker client version docker v1.13.1, it is func (cli *Client) ContainerExecAttach(ctx context.Context, execID string, config types.ExecConfig) (types.HijackedResponse, error) { , of course you can modify the code to satisfy your api version need.
Thank you for your comment. I have added execConfig := types.ExecStartCheck{Tty: false, Detach: false} and passed it to ContainerExecAttach. Good job, thank you for your work. This bounty is well deserved!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.