Allow passing ResultContext from server to the client through grpcerror

Signed-off-by: Kohei Tokunaga <ktokunaga.mail@gmail.com>
pull/1640/head
Kohei Tokunaga 2 years ago
parent 5c21e80a83
commit 1303715aba
No known key found for this signature in database
GPG Key ID: 6CE0A04690DB3FB3

@ -209,6 +209,9 @@ func buildTargets(ctx context.Context, dockerCli command.Cli, ng *store.NodeGrou
err = err1 err = err1
} }
if err != nil { if err != nil {
if res != nil {
err = wrapResultContext(err, res)
}
return nil, nil, err return nil, nil, err
} }
@ -379,3 +382,31 @@ func controllerUlimitOpt2DockerUlimit(u *controllerapi.UlimitOpt) *dockeropts.Ul
} }
return dockeropts.NewUlimitOpt(&values) return dockeropts.NewUlimitOpt(&values)
} }
// ResultContextError is an error type used for passing ResultContext from this package
// to the caller of RunBuild. This is only used when RunBuild fails with an error.
// When it succeeds without error, ResultContext is returned via non-error returned value.
//
// Caller can extract ResultContext from the error returned by RunBuild as the following:
//
// resp, res, buildErr := cbuild.RunBuild(ctx, req.Options, inR, statusChan)
// var re *cbuild.ResultContextError
// if errors.As(buildErr, &re) && re.ResultContext != nil {
// res = re.ResultContext
// }
type ResultContextError struct {
ResultContext *build.ResultContext
error
}
// Unwrap returns the original error.
func (e *ResultContextError) Unwrap() error {
return e.error
}
func wrapResultContext(wErr error, res *build.ResultContext) error {
if wErr == nil {
return nil
}
return &ResultContextError{ResultContext: res, error: wErr}
}

@ -0,0 +1,34 @@
package errdefs
import (
"github.com/containerd/typeurl/v2"
"github.com/moby/buildkit/util/grpcerrors"
)
func init() {
typeurl.Register((*Build)(nil), "github.com/docker/buildx", "errdefs.Build+json")
}
type BuildError struct {
Build
error
}
func (e *BuildError) Unwrap() error {
return e.error
}
func (e *BuildError) ToProto() grpcerrors.TypedErrorProto {
return &e.Build
}
func WrapBuild(err error, ref string) error {
if err == nil {
return nil
}
return &BuildError{Build: Build{Ref: ref}, error: err}
}
func (b *Build) WrapError(err error) error {
return &BuildError{error: err, Build: *b}
}

@ -0,0 +1,77 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: errdefs.proto
package errdefs
import (
fmt "fmt"
proto "github.com/gogo/protobuf/proto"
_ "github.com/moby/buildkit/solver/pb"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type Build struct {
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Build) Reset() { *m = Build{} }
func (m *Build) String() string { return proto.CompactTextString(m) }
func (*Build) ProtoMessage() {}
func (*Build) Descriptor() ([]byte, []int) {
return fileDescriptor_689dc58a5060aff5, []int{0}
}
func (m *Build) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Build.Unmarshal(m, b)
}
func (m *Build) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Build.Marshal(b, m, deterministic)
}
func (m *Build) XXX_Merge(src proto.Message) {
xxx_messageInfo_Build.Merge(m, src)
}
func (m *Build) XXX_Size() int {
return xxx_messageInfo_Build.Size(m)
}
func (m *Build) XXX_DiscardUnknown() {
xxx_messageInfo_Build.DiscardUnknown(m)
}
var xxx_messageInfo_Build proto.InternalMessageInfo
func (m *Build) GetRef() string {
if m != nil {
return m.Ref
}
return ""
}
func init() {
proto.RegisterType((*Build)(nil), "errdefs.Build")
}
func init() { proto.RegisterFile("errdefs.proto", fileDescriptor_689dc58a5060aff5) }
var fileDescriptor_689dc58a5060aff5 = []byte{
// 111 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x2d, 0x2a, 0x4a,
0x49, 0x4d, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0xa5, 0x74, 0xd2,
0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x73, 0xf3, 0x93, 0x2a, 0xf5, 0x93,
0x4a, 0x33, 0x73, 0x52, 0xb2, 0x33, 0x4b, 0xf4, 0x8b, 0xf3, 0x73, 0xca, 0x52, 0x8b, 0xf4, 0x0b,
0x92, 0xf4, 0xf3, 0x0b, 0xa0, 0xda, 0x94, 0x24, 0xb9, 0x58, 0x9d, 0x40, 0xf2, 0x42, 0x02, 0x5c,
0xcc, 0x41, 0xa9, 0x69, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x20, 0x66, 0x12, 0x1b, 0x58,
0x85, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x56, 0x52, 0x41, 0x91, 0x69, 0x00, 0x00, 0x00,
}

@ -0,0 +1,9 @@
syntax = "proto3";
package errdefs;
import "github.com/moby/buildkit/solver/pb/ops.proto";
message Build {
string Ref = 1;
}

@ -0,0 +1,3 @@
package errdefs
//go:generate protoc -I=. -I=../../vendor/ --gogo_out=plugins=grpc:. errdefs.proto

@ -9,6 +9,7 @@ import (
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build" cbuild "github.com/docker/buildx/controller/build"
"github.com/docker/buildx/controller/control" "github.com/docker/buildx/controller/control"
controllererrors "github.com/docker/buildx/controller/errdefs"
controllerapi "github.com/docker/buildx/controller/pb" controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes" "github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/ioset"
@ -40,11 +41,25 @@ func (b *localController) Build(ctx context.Context, options controllerapi.Build
} }
defer b.buildOnGoing.Store(false) defer b.buildOnGoing.Store(false)
resp, res, err := cbuild.RunBuild(ctx, b.dockerCli, options, in, progressMode, nil) resp, res, buildErr := cbuild.RunBuild(ctx, b.dockerCli, options, in, progressMode, nil)
if err != nil { if buildErr != nil {
return "", nil, err var re *cbuild.ResultContextError
if errors.As(buildErr, &re) && re.ResultContext != nil {
res = re.ResultContext
}
}
if res != nil {
b.buildConfig = buildConfig{
resultCtx: res,
buildOptions: &options,
}
if buildErr != nil {
buildErr = controllererrors.WrapBuild(buildErr, b.ref)
}
}
if buildErr != nil {
return "", nil, buildErr
} }
b.resultCtx = res
return b.ref, resp, nil return b.ref, resp, nil
} }

@ -13,6 +13,7 @@ import (
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/identity" "github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"google.golang.org/grpc" "google.golang.org/grpc"
@ -33,6 +34,8 @@ func NewClient(ctx context.Context, addr string) (*Client, error) {
grpc.WithContextDialer(dialer.ContextDialer), grpc.WithContextDialer(dialer.ContextDialer),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)), grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
grpc.WithUnaryInterceptor(grpcerrors.UnaryClientInterceptor),
grpc.WithStreamInterceptor(grpcerrors.StreamClientInterceptor),
} }
conn, err := grpc.DialContext(ctx, dialer.DialAddress(addr), gopts...) conn, err := grpc.DialContext(ctx, dialer.DialAddress(addr), gopts...)
if err != nil { if err != nil {

@ -24,6 +24,7 @@ import (
"github.com/docker/buildx/version" "github.com/docker/buildx/version"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -161,7 +162,10 @@ func serveCmd(dockerCli command.Cli) *cobra.Command {
if err != nil { if err != nil {
return err return err
} }
rpc := grpc.NewServer() rpc := grpc.NewServer(
grpc.UnaryInterceptor(grpcerrors.UnaryServerInterceptor),
grpc.StreamInterceptor(grpcerrors.StreamServerInterceptor),
)
controllerapi.RegisterControllerServer(rpc, b) controllerapi.RegisterControllerServer(rpc, b)
doneCh := make(chan struct{}) doneCh := make(chan struct{})
errCh := make(chan error, 1) errCh := make(chan error, 1)

@ -8,6 +8,8 @@ import (
"time" "time"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
cbuild "github.com/docker/buildx/controller/build"
controllererrors "github.com/docker/buildx/controller/errdefs"
"github.com/docker/buildx/controller/pb" "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/controller/processes" "github.com/docker/buildx/controller/processes"
"github.com/docker/buildx/util/ioset" "github.com/docker/buildx/util/ioset"
@ -177,24 +179,40 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp
// Build the specified request // Build the specified request
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
resp, res, err := m.buildFunc(ctx, req.Options, inR, statusChan) resp, res, buildErr := m.buildFunc(ctx, req.Options, inR, statusChan)
m.sessionMu.Lock() m.sessionMu.Lock()
if s, ok := m.session[ref]; ok { if s, ok := m.session[ref]; ok {
s.result = res if buildErr != nil {
s.cancelBuild = cancel var re *cbuild.ResultContextError
m.session[ref] = s if errors.As(buildErr, &re) && re.ResultContext != nil {
res = re.ResultContext
}
}
if res != nil {
s.result = res
s.cancelBuild = cancel
s.buildOptions = req.Options
m.session[ref] = s
if buildErr != nil {
buildErr = controllererrors.WrapBuild(buildErr, ref)
}
}
} else { } else {
m.sessionMu.Unlock() m.sessionMu.Unlock()
return nil, errors.Errorf("build: unknown key %v", ref) return nil, errors.Errorf("build: unknown key %v", ref)
} }
m.sessionMu.Unlock() m.sessionMu.Unlock()
if buildErr != nil {
return nil, buildErr
}
if resp == nil { if resp == nil {
resp = &client.SolveResponse{} resp = &client.SolveResponse{}
} }
return &pb.BuildResponse{ return &pb.BuildResponse{
ExporterResponse: resp.ExporterResponse, ExporterResponse: resp.ExporterResponse,
}, err }, nil
} }
func (m *Server) Status(req *pb.StatusRequest, stream pb.Controller_StatusServer) error { func (m *Server) Status(req *pb.StatusRequest, stream pb.Controller_StatusServer) error {

@ -8,6 +8,7 @@ require (
github.com/compose-spec/compose-go v1.9.0 github.com/compose-spec/compose-go v1.9.0
github.com/containerd/console v1.0.3 github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.7.0 github.com/containerd/containerd v1.7.0
github.com/containerd/typeurl/v2 v2.1.0
github.com/docker/cli v23.0.1+incompatible github.com/docker/cli v23.0.1+incompatible
github.com/docker/cli-docs-tool v0.5.1 github.com/docker/cli-docs-tool v0.5.1
github.com/docker/distribution v2.8.1+incompatible github.com/docker/distribution v2.8.1+incompatible
@ -82,7 +83,6 @@ require (
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect
github.com/containerd/continuity v0.3.0 // indirect github.com/containerd/continuity v0.3.0 // indirect
github.com/containerd/ttrpc v1.2.1 // indirect github.com/containerd/ttrpc v1.2.1 // indirect
github.com/containerd/typeurl/v2 v2.1.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/distribution/v3 v3.0.0-20221103125252-ebfa2a0ac0a9 // indirect github.com/distribution/distribution/v3 v3.0.0-20221103125252-ebfa2a0ac0a9 // indirect

Loading…
Cancel
Save