mirror of https://github.com/docker/buildx.git
build: add push support to docker driver
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
parent
aa7c17989b
commit
f68f42cb11
127
build/build.go
127
build/build.go
|
@ -3,6 +3,7 @@ package build
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/images"
|
"github.com/containerd/containerd/images"
|
||||||
"github.com/containerd/containerd/platforms"
|
"github.com/containerd/containerd/platforms"
|
||||||
|
@ -19,7 +21,9 @@ import (
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
clitypes "github.com/docker/cli/cli/config/types"
|
clitypes "github.com/docker/cli/cli/config/types"
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
dockerclient "github.com/docker/docker/client"
|
dockerclient "github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
|
@ -408,7 +412,9 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
|
||||||
opt.Exports[i].Type = "moby"
|
opt.Exports[i].Type = "moby"
|
||||||
if e.Attrs["push"] != "" {
|
if e.Attrs["push"] != "" {
|
||||||
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,8 +522,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
|
||||||
|
|
||||||
for k, opt := range opt {
|
for k, opt := range opt {
|
||||||
multiDriver := len(m[k]) > 1
|
multiDriver := len(m[k]) > 1
|
||||||
|
hasMobyDriver := false
|
||||||
for i, dp := range m[k] {
|
for i, dp := range m[k] {
|
||||||
d := drivers[dp.driverIndex].Driver
|
d := drivers[dp.driverIndex].Driver
|
||||||
|
if d.IsMobyDriver() {
|
||||||
|
hasMobyDriver = true
|
||||||
|
}
|
||||||
opt.Platforms = dp.platforms
|
opt.Platforms = dp.platforms
|
||||||
so, release, err := toSolveOpt(ctx, d, multiDriver, opt, w, func(name string) (io.WriteCloser, func(), error) {
|
so, release, err := toSolveOpt(ctx, d, multiDriver, opt, w, func(name string) (io.WriteCloser, func(), error) {
|
||||||
return newDockerLoader(ctx, docker, name, w)
|
return newDockerLoader(ctx, docker, name, w)
|
||||||
|
@ -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{}
|
resp = map[string]*client.SolveResponse{}
|
||||||
|
@ -675,6 +698,28 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
res[i] = rr
|
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
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -695,6 +740,86 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
|
||||||
return resp, nil
|
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) {
|
func createTempDockerfile(r io.Reader) (string, error) {
|
||||||
dir, err := ioutil.TempDir("", "dockerfile")
|
dir, err := ioutil.TempDir("", "dockerfile")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -36,6 +36,10 @@ func (d *Driver) IsMobyDriver() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Driver) Config() driver.InitConfig {
|
||||||
|
return d.InitConfig
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
||||||
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
||||||
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
||||||
|
|
|
@ -47,9 +47,8 @@ func (d *Driver) Features() map[driver.Feature]bool {
|
||||||
return map[driver.Feature]bool{
|
return map[driver.Feature]bool{
|
||||||
driver.OCIExporter: false,
|
driver.OCIExporter: false,
|
||||||
driver.DockerExporter: false,
|
driver.DockerExporter: false,
|
||||||
|
driver.CacheExport: false,
|
||||||
driver.CacheExport: false,
|
driver.MultiPlatform: false,
|
||||||
driver.MultiPlatform: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,3 +59,7 @@ func (d *Driver) Factory() driver.Factory {
|
||||||
func (d *Driver) IsMobyDriver() bool {
|
func (d *Driver) IsMobyDriver() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Driver) Config() driver.InitConfig {
|
||||||
|
return d.InitConfig
|
||||||
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ type Driver interface {
|
||||||
Client(ctx context.Context) (*client.Client, error)
|
Client(ctx context.Context) (*client.Client, error)
|
||||||
Features() map[Feature]bool
|
Features() map[Feature]bool
|
||||||
IsMobyDriver() bool
|
IsMobyDriver() bool
|
||||||
|
Config() InitConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func Boot(ctx context.Context, d Driver, pw progress.Writer) (*client.Client, error) {
|
func Boot(ctx context.Context, d Driver, pw progress.Writer) (*client.Client, error) {
|
||||||
|
|
|
@ -47,6 +47,9 @@ type Driver struct {
|
||||||
func (d *Driver) IsMobyDriver() bool {
|
func (d *Driver) IsMobyDriver() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
func (d *Driver) Config() driver.InitConfig {
|
||||||
|
return d.InitConfig
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
||||||
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Logger func(*client.SolveStatus)
|
||||||
type SubLogger interface {
|
type SubLogger interface {
|
||||||
Wrap(name string, fn func() error) error
|
Wrap(name string, fn func() error) error
|
||||||
Log(stream int, dt []byte)
|
Log(stream int, dt []byte)
|
||||||
|
SetStatus(*client.VertexStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Wrap(name string, l Logger, fn func(SubLogger) error) (err error) {
|
func Wrap(name string, l Logger, fn func(SubLogger) error) (err error) {
|
||||||
|
@ -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},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue