Allow ExitError type to be transmitted over GRPC
This will allow clients to retrieve exit error codes returned during a solve without parsing the error messages. Signed-off-by: Aaron Lehmann <alehmann@netflix.com>master
parent
1ba8f965c1
commit
890c3f77b6
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/gateway/client"
|
||||
gwerrdefs "github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/session"
|
||||
|
@ -451,8 +450,8 @@ func testClientGatewayContainerPID1Fail(t *testing.T, sb integration.Sandbox) {
|
|||
defer ctr.Release(ctx)
|
||||
err = pid1.Wait()
|
||||
|
||||
var exitError *gwerrdefs.ExitError
|
||||
require.True(t, errors.As(err, &exitError))
|
||||
var exitError *gatewayapi.ExitError
|
||||
require.ErrorAs(t, err, &exitError)
|
||||
require.Equal(t, uint32(99), exitError.ExitCode)
|
||||
|
||||
return nil, err
|
||||
|
@ -531,6 +530,9 @@ func testClientGatewayContainerPID1Exit(t *testing.T, sb integration.Sandbox) {
|
|||
|
||||
_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
|
||||
require.Error(t, err)
|
||||
var exitError *gatewayapi.ExitError
|
||||
require.ErrorAs(t, err, &exitError)
|
||||
require.Equal(t, uint32(137), exitError.ExitCode)
|
||||
// `exit code: 137` (ie sigkill)
|
||||
require.Regexp(t, "exit code: 137", err.Error())
|
||||
|
||||
|
@ -776,8 +778,8 @@ func testClientGatewayContainerPID1Tty(t *testing.T, sb integration.Sandbox) {
|
|||
prompt.SendExit(99)
|
||||
|
||||
err = pid1.Wait()
|
||||
var exitError *gwerrdefs.ExitError
|
||||
require.True(t, errors.As(err, &exitError))
|
||||
var exitError *gatewayapi.ExitError
|
||||
require.ErrorAs(t, err, &exitError)
|
||||
require.Equal(t, uint32(99), exitError.ExitCode)
|
||||
|
||||
return &client.Result{}, err
|
||||
|
@ -920,8 +922,8 @@ func testClientGatewayContainerExecTty(t *testing.T, sb integration.Sandbox) {
|
|||
prompt.SendExit(99)
|
||||
|
||||
err = pid2.Wait()
|
||||
var exitError *gwerrdefs.ExitError
|
||||
require.True(t, errors.As(err, &exitError))
|
||||
var exitError *gatewayapi.ExitError
|
||||
require.ErrorAs(t, err, &exitError)
|
||||
require.Equal(t, uint32(99), exitError.ExitCode)
|
||||
|
||||
return &client.Result{}, err
|
||||
|
@ -929,6 +931,9 @@ func testClientGatewayContainerExecTty(t *testing.T, sb integration.Sandbox) {
|
|||
|
||||
_, err = c.Build(ctx, SolveOpt{}, product, b, nil)
|
||||
require.Error(t, err)
|
||||
var exitError *gatewayapi.ExitError
|
||||
require.ErrorAs(t, err, &exitError)
|
||||
require.Equal(t, uint32(99), exitError.ExitCode)
|
||||
require.Regexp(t, "exit code: 99", err.Error())
|
||||
|
||||
inputW.Close()
|
||||
|
@ -1145,7 +1150,7 @@ func testClientGatewayExecError(t *testing.T, sb integration.Sandbox) {
|
|||
require.Error(t, solveErr)
|
||||
|
||||
var se *errdefs.SolveError
|
||||
require.True(t, errors.As(solveErr, &se))
|
||||
require.ErrorAs(t, solveErr, &se)
|
||||
require.Len(t, se.InputIDs, tt.NumMounts)
|
||||
require.Len(t, se.MountIDs, tt.NumMounts)
|
||||
|
||||
|
@ -1265,7 +1270,7 @@ func testClientGatewaySlowCacheExecError(t *testing.T, sb integration.Sandbox) {
|
|||
require.Error(t, solveErr)
|
||||
|
||||
var se *errdefs.SolveError
|
||||
require.True(t, errors.As(solveErr, &se))
|
||||
require.ErrorAs(t, solveErr, &se)
|
||||
|
||||
_, ok := se.Solve.Op.Op.(*pb.Op_Exec)
|
||||
require.True(t, ok)
|
||||
|
@ -1398,7 +1403,7 @@ func testClientGatewayExecFileActionError(t *testing.T, sb integration.Sandbox)
|
|||
require.Error(t, err)
|
||||
|
||||
var se *errdefs.SolveError
|
||||
require.True(t, errors.As(err, &se))
|
||||
require.ErrorAs(t, err, &se)
|
||||
require.Len(t, se.Solve.InputIDs, tt.NumInputs)
|
||||
|
||||
// There is one output for every action in the fileop that failed.
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
"github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
|
@ -388,11 +388,11 @@ func (w *containerdExecutor) runProcess(ctx context.Context, p containerd.Proces
|
|||
cancel()
|
||||
}
|
||||
if status.ExitCode() != 0 {
|
||||
exitErr := &errdefs.ExitError{
|
||||
exitErr := &gatewayapi.ExitError{
|
||||
ExitCode: status.ExitCode(),
|
||||
Err: status.Error(),
|
||||
}
|
||||
if status.ExitCode() == errdefs.UnknownExitStatus && status.Error() != nil {
|
||||
if status.ExitCode() == gatewayapi.UnknownExitStatus && status.Error() != nil {
|
||||
exitErr.Err = errors.Wrap(status.Error(), "failure waiting for process")
|
||||
}
|
||||
select {
|
||||
|
|
|
@ -4,13 +4,13 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
)
|
||||
|
||||
func TestContainerdUnknownExitStatus(t *testing.T) {
|
||||
// There are assumptions in the containerd executor that the UnknownExitStatus
|
||||
// used in errdefs.ExitError matches the variable in the containerd package.
|
||||
if containerd.UnknownExitStatus != errdefs.UnknownExitStatus {
|
||||
if containerd.UnknownExitStatus != gatewayapi.UnknownExitStatus {
|
||||
t.Fatalf("containerd.UnknownExitStatus != errdefs.UnknownExitStatus")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/moby/buildkit/executor"
|
||||
"github.com/moby/buildkit/executor/oci"
|
||||
"github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/moby/buildkit/util/network"
|
||||
|
@ -340,13 +340,13 @@ func (w *runcExecutor) Run(ctx context.Context, id string, root executor.Mount,
|
|||
|
||||
func exitError(ctx context.Context, err error) error {
|
||||
if err != nil {
|
||||
exitErr := &errdefs.ExitError{
|
||||
ExitCode: errdefs.UnknownExitStatus,
|
||||
exitErr := &gatewayapi.ExitError{
|
||||
ExitCode: gatewayapi.UnknownExitStatus,
|
||||
Err: err,
|
||||
}
|
||||
var runcExitError *runc.ExitError
|
||||
if errors.As(err, &runcExitError) {
|
||||
exitErr = &errdefs.ExitError{
|
||||
exitErr = &gatewayapi.ExitError{
|
||||
ExitCode: uint32(runcExitError.Status),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/frontend"
|
||||
gwclient "github.com/moby/buildkit/frontend/gateway/client"
|
||||
gwerrdefs "github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
pb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/identity"
|
||||
"github.com/moby/buildkit/session"
|
||||
|
@ -1168,10 +1167,10 @@ func (lbf *llbBridgeForwarder) ExecProcess(srv pb.LLBBridge_ExecProcessServer) e
|
|||
err := proc.Wait()
|
||||
|
||||
var statusCode uint32
|
||||
var exitError *gwerrdefs.ExitError
|
||||
var exitError *pb.ExitError
|
||||
var statusError *rpc.Status
|
||||
if err != nil {
|
||||
statusCode = gwerrdefs.UnknownExitStatus
|
||||
statusCode = pb.UnknownExitStatus
|
||||
st, _ := status.FromError(grpcerrors.ToGRPC(err))
|
||||
stp := st.Proto()
|
||||
statusError = &rpc.Status{
|
||||
|
|
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/frontend/gateway/client"
|
||||
"github.com/moby/buildkit/frontend/gateway/errdefs"
|
||||
pb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/identity"
|
||||
opspb "github.com/moby/buildkit/solver/pb"
|
||||
|
@ -883,8 +882,8 @@ func (ctr *container) Start(ctx context.Context, req client.StartRequest) (clien
|
|||
Message: exit.Error.Message,
|
||||
Details: convertGogoAny(exit.Error.Details),
|
||||
}))
|
||||
if exit.Code != errdefs.UnknownExitStatus {
|
||||
exitError = &errdefs.ExitError{ExitCode: exit.Code, Err: exitError}
|
||||
if exit.Code != pb.UnknownExitStatus {
|
||||
exitError = &pb.ExitError{ExitCode: exit.Code, Err: exitError}
|
||||
}
|
||||
} else if serverDone := msg.GetDone(); serverDone != nil {
|
||||
return exitError
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package errdefs
|
||||
package moby_buildkit_v1_frontend //nolint:golint
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnknownExitStatus might be returned in (*ExitError).ExitCode via
|
||||
|
@ -12,6 +17,10 @@ const (
|
|||
UnknownExitStatus = 255
|
||||
)
|
||||
|
||||
func init() {
|
||||
typeurl.Register((*ExitMessage)(nil), "github.com/moby/buildkit", "gatewayapi.ExitMessage+json")
|
||||
}
|
||||
|
||||
// ExitError will be returned when the container process exits with a non-zero
|
||||
// exit code.
|
||||
type ExitError struct {
|
||||
|
@ -19,6 +28,12 @@ type ExitError struct {
|
|||
Err error
|
||||
}
|
||||
|
||||
func (err *ExitError) ToProto() grpcerrors.TypedErrorProto {
|
||||
return &ExitMessage{
|
||||
Code: err.ExitCode,
|
||||
}
|
||||
}
|
||||
|
||||
func (err *ExitError) Error() string {
|
||||
if err.Err != nil {
|
||||
return err.Err.Error()
|
||||
|
@ -27,8 +42,12 @@ func (err *ExitError) Error() string {
|
|||
}
|
||||
|
||||
func (err *ExitError) Unwrap() error {
|
||||
if err.Err == nil {
|
||||
return fmt.Errorf("exit code: %d", err.ExitCode)
|
||||
}
|
||||
return err.Err
|
||||
}
|
||||
|
||||
func (e *ExitMessage) WrapError(err error) error {
|
||||
return &ExitError{
|
||||
Err: err,
|
||||
ExitCode: e.Code,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue