Add docker/docker to your dependencies

#go  #mod 

The problem

If you are using go modules and want to use the client from docker/docker in your code you would do something like this:

package main

import (
    "context"
    "fmt"

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

func main() {
    c, err := client.NewClientWithOpts(client.FromEnv)
    if err != nil {
        fmt.Println(err)
        return
    }

    containers, err := c.ContainerList(context.Background(), types.ContainerListOptions{})
    if err != nil {
        fmt.Println(err)
        return
    }

    for _, container := range containers {
        fmt.Println(container.ID)
    }
}

If you then try and build your code you will be greeted with a nice error messsage:

./main.go:12:15: undefined: client.NewClientWithOpts
./main.go:12:40: undefined: client.FromEnv

What just happened? If you look at your go.mod file you would see something like this:

require (
    ...
    github.com/docker/docker v1.13.1 // indirect
    ...
)

It its great wisdom, go mod decided to take the v1.13.1 tag because this is the latest tag that happens to be semver valid. The problem with this tag is that it was created in 2017. And client.NewClientWithOpts did not exist in 2017.

The solution

Here’s how to fix this shenanigans: add github.com/docker/docker master in your go.mod file and execute go mod tidy. If you now look at your go.mod file you will see something like this:

github.com/docker/docker v17.12.0-ce-rc1.0.20200508181053-298ba5b13150+incompatible

I am unsure why it takes the v17.12.0-ce-rc1 tag but the 298ba5b13150 sha is indeed the sha of the latest commit on docker/docker at this time

If you now try to build the code above it will work as expected.

Note: you could have also changed your code to create a client with c, err := client.NewEnvClient() and the code would have worked, this is because the docker team made it so that even an older client can talk to the latest engine, and this is kinda awesome.

Recap

Why this happens? The reason is twofold:

This means that even if docker/docker released a bunch of new versions since 2017, its way of naming tags (latest tag right now is v19.03.8) makes it incompatible with the way go mod thinks you should name your tags. Fortunately there is a solution, I just wished life was easier sometimes.