CaptainRR
CaptainRR

Reputation: 421

Grab output from container in Docker SDK

I'm trying to run a container using Docker SDK for golang and I can't get the output from the container. I'm using the following code for that that actually runs the container, but doesn't sends back stderr and stdout of the application. Can you advice what I'm doing wrong?

type dckr struct {
    cli      *client.Client
    username string
    password string
    addr     string
    ctx      context.Context
}

func (d *dckr) Run(containername string, image string, command []string, bind []string, stdout io.Writer, stderr io.Writer) error {
    log.Printf("[Create] %s -> %s \n", image, containername)

    res, err := d.cli.ContainerCreate(
        d.ctx,
        &container.Config{
            User:         "root",
            AttachStdout: true,
            AttachStderr: true,
            Image:        image,
            Cmd:          command,
        },
        &container.HostConfig{
            AutoRemove: true,
            Binds:      bind,
        },
        &network.NetworkingConfig{},
        containername,
    )

    if err != nil {
        log.Println("[Create] Failed. %s", err)
        return err
    }
    defer d.cli.ContainerRemove(d.ctx, res.ID, types.ContainerRemoveOptions{Force: true})

    log.Printf("[Create] id: %s \n", res.ID)
    for wrn := range res.Warnings {
        log.Printf("[Create] %s \n", wrn)
    }

    rsp, err := d.cli.ContainerAttach(d.ctx, containername, types.ContainerAttachOptions{
        Stream: false,
        Stdout: true,
        Stderr: true,
        Logs:   true,
    })

    if err != nil {
        log.Printf("[Attach] Fail. %s \n", err)
        return err
    }
    log.Printf("[Attach] %s", res.ID)
    defer rsp.Close()

    err = d.cli.ContainerStart(d.ctx, res.ID, types.ContainerStartOptions{})
    if err != nil {
        log.Printf("[Run] Fail. %s \n", err)
        return err
    }

    _, err = stdcopy.StdCopy(stdout, stderr, rsp.Reader)
    return err
}

Upvotes: 0

Views: 1225

Answers (1)

Santosh Kumar
Santosh Kumar

Reputation: 27875

The question was asked in 2017 and I'm answering it in 2022. I understand the APIs might have changed, but I have landed on a similar boat.

Let's not talk about how to start a container as you seem to have already done that. Here is my code to fetch the logs from a given container:

// GetLogs return logs from the container io.ReadCloser. It's the caller duty
// duty to do a stdcopy.StdCopy. Any other method might render unknown
// unicode character as log output has both stdout and stderr. That starting
// has info if that line is stderr or stdout.
func GetLogs(ctx context.Context, cli *client.Client, contName string) (logOutput io.ReadCloser) {
    options := types.ContainerLogsOptions{ShowStdout: true}

    out, err := cli.ContainerLogs(ctx, contName, options)
    if err != nil {
        panic(err)
    }

    return out
}

You can call this GetLogs from another routine. I am saving both of the stream in specific files. But you may want to use os.Stdout and os.Stderr if you just want to see them on your terminal:

func main() {
    stdoutLog, _ := os.Create("yourContainerName.log")
    defer stdoutLog.Close()
    stderrLog, _ := os.Create("yourContainerName.err")
    defer stderrLog.Close()

    var stdout bytes.Buffer
    var stderr bytes.Buffer
    
    containerLog := docker.GetLogs(ctx, dc, "yourContainerName")
    stdcopy.StdCopy(&stdout, &stderr, containerLog)
    stdoutLog.Write(stdout.Bytes())
    stderrLog.Write(stderr.Bytes())
}

Let me know the second part if you still have confusion. I'm happy to help as I had a similar problem.

Upvotes: 2

Related Questions