0

This defeats me. I am not sure what I am doing wrong. There is so less documentation that searching did not produce good result. I will be happy to see what is the reason behind this strange behaviour.

I am on a MAC (10.11.6) and I am running docker for MAC (beta)

Here is the code I am trying to run

package main

import (
    "fmt"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
    "golang.org/x/net/context"
)

func main() {
    defaultHeaders := map[string]string{"User-Agent": "ego-v-0.0.1"}
    cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
    options := types.ImageBuildOptions{
        Dockerfile:     "/path/to/my/Dockerfile",
        SuppressOutput: false,
        Remove:         true,
        ForceRemove:    true,
        PullParent:     true}
    buildResponse, err := cli.ImageBuild(context.Background(), nil, options)
    if err != nil {
        fmt.Printf("%s", err.Error())
    }
    fmt.Printf("%s", buildResponse.OSType)
}

This gives me this error -

Error response from daemon: {"message":"Cannot locate specified Dockerfile: /path/to/my/Dockerfile"}

Whereas when I run this command (from the same directory where my Go code is)

docker build /path/to/my

It works absolutely fine.

What am I doing wrong? I feel like banging my head against a wall now. Please help.

------------ EDIT / ADD ------------ I ended up doing this -

package main

import (
    "archive/tar"
    "fmt"
    "io"
    "os"
    "path/filepath"
    "strings"

    "golang.org/x/net/context"

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

func tarit(source, target string) error {
    filename := filepath.Base(source)
    target = filepath.Join(target, fmt.Sprintf("%s.tar", filename))
    fmt.Println(target)
    tarfile, err := os.Create(target)
    if err != nil {
        return err
    }
    defer tarfile.Close()

    tarball := tar.NewWriter(tarfile)
    defer tarball.Close()

    info, err := os.Stat(source)
    if err != nil {
        return nil
    }

    var baseDir string
    if info.IsDir() {
        baseDir = filepath.Base(source)
    }

    return filepath.Walk(source,
        func(path string, info os.FileInfo, err error) error {
            if err != nil {
                return err
            }
            header, err := tar.FileInfoHeader(info, info.Name())
            if err != nil {
                return err
            }
            if baseDir != "" {
                header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
            }

            if err := tarball.WriteHeader(header); err != nil {
                return err
            }

            if info.IsDir() {
                return nil
            }

            file, err := os.Open(path)
            if err != nil {
                return err
            }
            defer file.Close()
            _, err = io.Copy(tarball, file)
            return err
        })
}

func main() {
    tarit("/dir/with/files/and/dockerfile", "repo")
    dockerBuildContext, err := os.Open("./repo.tar")
    defer dockerBuildContext.Close()
    defaultHeaders := map[string]string{"User-Agent": "ego-v-0.0.1"}
    cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
    options := types.ImageBuildOptions{
        Dockerfile:     "repo/Dockerfile",
        SuppressOutput: false,
        Remove:         true,
        ForceRemove:    true,
        PullParent:     true}
    buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, options)
    if err != nil {
        fmt.Printf("%s", err.Error())
    }
    fmt.Printf("********* %s **********", buildResponse.OSType)
}

Now it is not complaining about anything and I can see that the tar is getting made properly and the last println is printing

********* linux **********

Which is a reply from the server. But it does not build anything. I understand that reply is almost immediate as under the hood it is just a POST request. But not sure why it is not building anything although.

SRC
  • 2,123
  • 3
  • 31
  • 44

2 Answers2

0

The Dockerfile has to be part of the build context that gets TARd and sent to the engine. You shouldn't use an absolute path to the Dockerfile, it must be relative to the context, so you just pass the name.

I don't think the CLI command you demonstrate is right - unless Dockerfile is actually a directory name in your case:

> ls /tmp/path/to/context 
Dockerfile
> docker build /tmp/path/to/context/Dockerfile
unable to prepare context: context must be a directory: /tmp/path/to/context/Dockerfile

When you send just the path (which contains the Dockerfile file), that works:

> docker build /tmp/path/to/context
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM alpine
 ---> ee4603260daa
Successfully built ee4603260daa
Elton Stoneman
  • 17,906
  • 5
  • 47
  • 44
  • There was a slight error in my post. When I am using the command from command line I am not adding the Dockerfile at the end of it. I am giving just the path to the dockerfile and the client automatically detects the Dockerfile present in that path. I expected the same to happen with the code. But it is not. So essentially what works for the default client does not work for y code. This is what is strange. – SRC Oct 06 '16 at 10:37
  • The CLI does an extra step - it TARs the context directory before sending it to the API to build. That's the step you're missing - you're sending `nil` as the context. See [this answer](http://stackoverflow.com/questions/38804313/build-docker-image-from-go-code) which shows you how to send the build context in the Go client. – Elton Stoneman Oct 06 '16 at 11:03
  • I took your suggestion and after some time of trial and error I ended up to the code I have posted under edit. Now it does not complain about anything but it is not building anything also. I am trying to look into this. Problem is, there is absolutely no documentation on this that I can find. So strange! – SRC Oct 06 '16 at 17:40
  • I posted an answer that worked for me. Please check it. – SRC Oct 07 '16 at 17:55
0

The working code to this problem. For anyone who will probably be stuck like me -

package main

import (
    "fmt"
    "io/ioutil"
    "os"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
    "github.com/jhoonb/archivex"
    "golang.org/x/net/context"
)

func main() {
    tar := new(archivex.TarFile)
    tar.Create("/path/to/tar/archieve")
    tar.AddAll("/path/to/base/folder", false)
    tar.Close()
    dockerBuildContext, err := os.Open("/path/to/tar/archieve.tar")
    defer dockerBuildContext.Close()
    defaultHeaders := map[string]string{"Content-Type": "application/tar"}
    cli, _ := client.NewClient("unix:///var/run/docker.sock", "v1.24", nil, defaultHeaders)
    options := types.ImageBuildOptions{
        SuppressOutput: true,
        Remove:         true,
        ForceRemove:    true,
        PullParent:     true,
        Tags:           []string{"xxx"}}
    buildResponse, err := cli.ImageBuild(context.Background(), dockerBuildContext, options)
    defer buildResponse.Body.Close()
    if err != nil {
        fmt.Printf("%s", err.Error())
    }
    //time.Sleep(5000 * time.Millisecond)
    fmt.Printf("********* %s **********", buildResponse.OSType)
    response, err := ioutil.ReadAll(buildResponse.Body)
    if err != nil {
        fmt.Printf("%s", err.Error())
    }
    fmt.Println(string(response))
}

Be careful that a lot of error checks are not done in this code. This is just a working code where I am using docker client lib successfully.

Let me know what you think.

Thanks to -

"github.com/jhoonb/archivex"

SRC
  • 2,123
  • 3
  • 31
  • 44