I need build some Docker image directly from my Golang application. I spent some time on Google but I didn’t find any fully functional example of Goland code…
And then I had to write my own implementation of this functionality.
Here we go!
package main
import (
"bufio"
"context"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
func main() {
if len(os.Args) != 3 {
fmt.Println("bad num of arguments:\n\t1. = dir with image content\n\t2. = image name")
os.Exit(0)
}
msg, err := buildImage(os.Args[1], os.Args[2])
if err != nil {
log.Fatal(err)
}
fmt.Println(msg)
}
func createTar(srcDir, tarFIle string) error {
/* #nosec */
c := exec.Command("tar", "-cf", tarFIle, "-C", srcDir, ".")
if err := c.Run(); err != nil {
return nil
}
return nil
}
func tempFileName(prefix, suffix string) (string, error) {
randBytes := make([]byte, 16)
if _, err := rand.Read(randBytes); err != nil {
return "", err
}
return filepath.Join(os.TempDir(), prefix+hex.EncodeToString(randBytes)+suffix), nil
}
func buildImage(dir, name string) ([]string, error) {
tarFile, err := tempFileName("docker-", ".image")
if err != nil {
return nil, err
}
defer os.Remove(tarFile)
if err := createTar(dir, tarFile); err != nil {
return nil, err
}
/* #nosec */
dockerFileTarReader, err := os.Open(tarFile)
if err != nil {
return nil, err
}
defer dockerFileTarReader.Close()
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}
defer cli.Close()
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(300)*time.Second)
defer cancel()
buildArgs := make(map[string]*string)
PWD, err := os.Getwd()
if err != nil {
return nil, err
}
defer os.Chdir(PWD)
if err := os.Chdir(dir); err != nil {
return nil, err
}
resp, err := cli.ImageBuild(
ctx,
dockerFileTarReader,
types.ImageBuildOptions{
Dockerfile: "./Dockerfile",
Tags: []string{name},
NoCache: true,
Remove: true,
BuildArgs: buildArgs,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()
var messages []string
rd := bufio.NewReader(resp.Body)
for {
n, _, err := rd.ReadLine()
if err != nil && err == io.EOF {
break
} else if err != nil {
return messages, err
}
messages = append(messages, string(n))
}
return messages, nil
}