Search code examples
powershelldockergodocker-for-windows

Execute powershell command in running container via Docker API


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.


Solution

  • 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.