How you can’t use Docker’s Go client

I wanted to build a tool which simplify and make faster command executions in docker containers. Here it is, where we can have three word to run a command in a docker container. So I want to tell a funny story about how I got into Docker’s Go client library.

Disclaimer: I’m not stupid and don’t want to blame anybody, I just wanted to make fun of how I found the solution!

Update 1: I found a related issue.

I read about how I can work with the Docker’s Go library, so as many Go developers I wanted to see what’s inside the GoDoc. So as it is shown in the example it looks like this:

package main

import (
"context"
"fmt"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)

func main() {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
panic(err)
}

containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{})
if err != nil {
panic(err)
}

for _, container := range containers {
fmt.Printf("%s %s\n", container.ID[:10], container.Image)
}
}

That’s looks easy. Let’s import the package then.
go get github.com/docker/docker

Hm…

Let’s see what we can have instead of this.

Looks similar. Let’s use the client.NewEnvClient(), but it’s ran into issues. So I started to dig into the code. I thought it would be something like the DOCKER_HOST environment variable, so I started to search for that. And yes, I found it.

host := os.Getenv("DOCKER_HOST")

Cool. Let’s look what’s inside this environment variable.

$ echo $DOCKER_HOST$

It’s empty. This answers everything. But what should I put into this environment variable? I have searched for that and quickly found the best solution the human race can imagine: localhost:2375. Localhost what? I’m not stupid, so I used telnet.

$ telnet localhost 2375
Trying ::1...
telnet: connect to address ::1: Connection refused

Connection refused, oh you don’t say. But wait, there is a DefaultDockerHost. Then I will go with this, so I will use the client.NewClient(...). But it wants extra parameters like version, http.Client and http headers. There is a DefaultVersion as well. Cool, let’s pass them and create an http.Client, however the default host is unix://var/run/docker.sock, but I will pass that http.Client if it wants it.

$ go run main.go 
panic: unable to verify TLS configuration, invalid transport <nil>

I’m an experienced Go developer. &tls.Config{InsecureSkipVerify: true}
Let’s try it again.

$ go run main.go 
panic: error during connect: Get https://%2Fvar%2Frun%2Fdocker.sock/v1.25/containers/json?limit=0: dial tcp: lookup /var/run/docker.sock: no such host

Frankly, I’m not surprised. Let’s skip this dump step, I don’t want to look stupid. After I have removed the http.Client I finally got the cli and the code above worked properly.

Finally.

Don’t be so happy. That was only the connection. I want to run a command inside a Docker container. I search for that. Nothing relevant found, so let’s see the code.

Cool, but what should I use?

ExecAttach: Attaches a connection to an exec process in the server.
ExecStart: Starts an exec process already created in the docker host.
ExecCreate: Creates a new exec configuration to run an exec process.
ExecInspect: Returns information about a specific exec process on the docker host.

That’s not that hard, Create and Start. Nobody makes mistakes with that.

May I ask for a response? Let’s dig into the code once more. AHA!

So we know that the response which I want will be closed. Let’s see what else we have. I don’t exactly know what attach means in a programming language library, but it returns something at least. HijackResponse, hm. I’m ok with it, but it’s maybe because of the lack of my English. I don’t know what is this. No worries, comments will describe it.

Anyway, there is a Reader in it. Finally I will get some response.

I added the Cmd on both places just to make sure. Let’s try and finish it. Thanks for the ioutil.ReadAll I could read the response and I could finish it.

$ go run main.goResponse: 

I can’t believe it. What else can I add to get some F*** response?

Yes, sure, attach it.

Thank you for being here.

Gopher, Rustacean, Hobby Hacker

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store