Skip to content

Commit

Permalink
Merge pull request docker#442 from tonistiigi/moby-push
Browse files Browse the repository at this point in the history
build: add push support to docker driver
  • Loading branch information
tonistiigi authored Dec 8, 2020
2 parents ea1a71d + f68f42c commit 33e3ca5
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 4 deletions.
127 changes: 126 additions & 1 deletion build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package build
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
Expand All @@ -11,6 +12,7 @@ import (
"strconv"
"strings"
"sync"
"time"

"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
Expand All @@ -19,7 +21,9 @@ import (
"github.com/docker/buildx/util/progress"
clitypes "github.com/docker/cli/cli/config/types"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/pkg/urlutil"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
Expand Down Expand Up @@ -414,7 +418,9 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
opt.Exports[i].Type = "moby"
if e.Attrs["push"] != "" {
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
return nil, nil, errors.Errorf("auto-push is currently not implemented for docker driver, please create a new builder instance")
if ok, _ := strconv.ParseBool(e.Attrs["push-by-digest"]); ok {
return nil, nil, errors.Errorf("push-by-digest is currently not implemented for docker driver, please create a new builder instance")
}
}
}
}
Expand Down Expand Up @@ -522,8 +528,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do

for k, opt := range opt {
multiDriver := len(m[k]) > 1
hasMobyDriver := false
for i, dp := range m[k] {
d := drivers[dp.driverIndex].Driver
if d.IsMobyDriver() {
hasMobyDriver = true
}
opt.Platforms = dp.platforms
so, release, err := toSolveOpt(ctx, d, multiDriver, opt, w, func(name string) (io.WriteCloser, func(), error) {
return newDockerLoader(ctx, docker, name, w)
Expand All @@ -543,6 +553,19 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
})
}
}

// validate for multi-node push
if hasMobyDriver && multiDriver {
for _, dp := range m[k] {
for _, e := range dp.so.Exports {
if e.Type == "moby" {
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
return nil, errors.Errorf("multi-node push can't currently be performed with the docker driver, please switch to a different driver")
}
}
}
}
}
}

resp = map[string]*client.SolveResponse{}
Expand Down Expand Up @@ -681,6 +704,28 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
return err
}
res[i] = rr

d := drivers[dp.driverIndex].Driver
if d.IsMobyDriver() {
for _, e := range so.Exports {
if e.Type == "moby" && e.Attrs["push"] != "" {
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
pushNames = e.Attrs["name"]
if pushNames == "" {
return errors.Errorf("tag is needed when pushing to registry")
}
pw := progress.ResetTime(pw)
for _, name := range strings.Split(pushNames, ",") {
if err := progress.Wrap(fmt.Sprintf("pushing %s with docker", name), pw.Write, func(l progress.SubLogger) error {
return pushWithMoby(ctx, d, name, l)
}); err != nil {
return err
}
}
}
}
}
}
return nil
})

Expand All @@ -701,6 +746,86 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
return resp, nil
}

func pushWithMoby(ctx context.Context, d driver.Driver, name string, l progress.SubLogger) error {
api := d.Config().DockerAPI
if api == nil {
return errors.Errorf("invalid empty Docker API reference") // should never happen
}
creds, err := imagetools.RegistryAuthForRef(name, d.Config().Auth)
if err != nil {
return err
}

rc, err := api.ImagePush(ctx, name, types.ImagePushOptions{
RegistryAuth: creds,
})
if err != nil {
return err
}

started := map[string]*client.VertexStatus{}

defer func() {
for _, st := range started {
if st.Completed == nil {
now := time.Now()
st.Completed = &now
l.SetStatus(st)
}
}
}()

dec := json.NewDecoder(rc)
var parsedError error
for {
var jm jsonmessage.JSONMessage
if err := dec.Decode(&jm); err != nil {
if parsedError != nil {
return parsedError
}
if err == io.EOF {
break
}
return err
}
if jm.ID != "" {
id := "pushing layer " + jm.ID
st, ok := started[id]
if !ok {
if jm.Progress != nil || jm.Status == "Pushed" {
now := time.Now()
st = &client.VertexStatus{
ID: id,
Started: &now,
}
started[id] = st
} else {
continue
}
}
st.Timestamp = time.Now()
if jm.Progress != nil {
st.Current = jm.Progress.Current
st.Total = jm.Progress.Total
}
if jm.Error != nil {
now := time.Now()
st.Completed = &now
}
if jm.Status == "Pushed" {
now := time.Now()
st.Completed = &now
st.Current = st.Total
}
l.SetStatus(st)
}
if jm.Error != nil {
parsedError = jm.Error
}
}
return nil
}

func createTempDockerfile(r io.Reader) (string, error) {
dir, err := ioutil.TempDir("", "dockerfile")
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions driver/docker-container/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func (d *Driver) IsMobyDriver() bool {
return false
}

func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}

func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
Expand Down
9 changes: 6 additions & 3 deletions driver/docker/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ func (d *Driver) Features() map[driver.Feature]bool {
return map[driver.Feature]bool{
driver.OCIExporter: false,
driver.DockerExporter: false,

driver.CacheExport: false,
driver.MultiPlatform: false,
driver.CacheExport: false,
driver.MultiPlatform: false,
}
}

Expand All @@ -60,3 +59,7 @@ func (d *Driver) Factory() driver.Factory {
func (d *Driver) IsMobyDriver() bool {
return true
}

func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}
1 change: 1 addition & 0 deletions driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type Driver interface {
Client(ctx context.Context) (*client.Client, error)
Features() map[Feature]bool
IsMobyDriver() bool
Config() InitConfig
}

func Boot(ctx context.Context, d Driver, pw progress.Writer) (*client.Client, error) {
Expand Down
3 changes: 3 additions & 0 deletions driver/kubernetes/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ type Driver struct {
func (d *Driver) IsMobyDriver() bool {
return false
}
func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}

func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
Expand Down
8 changes: 8 additions & 0 deletions util/progress/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Logger func(*client.SolveStatus)
type SubLogger interface {
Wrap(name string, fn func() error) error
Log(stream int, dt []byte)
SetStatus(*client.VertexStatus)
}

func Wrap(name string, l Logger, fn func(SubLogger) error) (err error) {
Expand Down Expand Up @@ -88,3 +89,10 @@ func (sl *subLogger) Log(stream int, dt []byte) {
}},
})
}

func (sl *subLogger) SetStatus(st *client.VertexStatus) {
st.Vertex = sl.dgst
sl.logger(&client.SolveStatus{
Statuses: []*client.VertexStatus{st},
})
}

0 comments on commit 33e3ca5

Please sign in to comment.