Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: add push support to docker driver #442

Merged
merged 1 commit into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -408,7 +412,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 @@ -516,8 +522,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 @@ -537,6 +547,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 @@ -675,6 +698,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 @@ -695,6 +740,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},
})
}