diff --git a/components/content-service/pkg/initializer/initializer.go b/components/content-service/pkg/initializer/initializer.go index 3d0b3c84b21acc..e3d158ba91f035 100644 --- a/components/content-service/pkg/initializer/initializer.go +++ b/components/content-service/pkg/initializer/initializer.go @@ -142,7 +142,7 @@ func NewFromRequest(ctx context.Context, loc string, rs storage.DirectDownloader initializer = &EmptyInitializer{} } if err != nil { - return nil, status.Error(codes.Internal, fmt.Sprintf("cannot initialize workspace: %v", err)) + return nil, status.Error(codes.InvalidArgument, err.Error()) } return initializer, nil } diff --git a/components/ws-daemon/cmd/content-initializer/main.go b/components/ws-daemon/cmd/content-initializer/main.go index a6b49263d77fbb..e06f01a1281945 100644 --- a/components/ws-daemon/cmd/content-initializer/main.go +++ b/components/ws-daemon/cmd/content-initializer/main.go @@ -5,6 +5,7 @@ package main import ( + "fmt" "os" "github.com/gitpod-io/gitpod/common-go/log" @@ -18,6 +19,9 @@ func main() { err := content.RunInitializerChild() if err != nil { + errfd := os.NewFile(uintptr(3), "errout") + _, _ = fmt.Fprintf(errfd, err.Error()) + os.Exit(42) } } diff --git a/components/ws-daemon/pkg/content/initializer.go b/components/ws-daemon/pkg/content/initializer.go index 44e0b5337ebef4..9e3ec91e0a37e6 100644 --- a/components/ws-daemon/pkg/content/initializer.go +++ b/components/ws-daemon/pkg/content/initializer.go @@ -9,12 +9,14 @@ import ( "context" "encoding/json" "errors" + "io/ioutil" "net/http" "os" "os/exec" "path/filepath" "strings" "syscall" + "time" "github.com/google/uuid" "github.com/opencontainers/runc/libcontainer/specconv" @@ -237,7 +239,19 @@ func RunInitializer(ctx context.Context, destination string, initializer *csapi. name = "init-ws-" + opts.OWI.InstanceID } - args = append(args, "--log-format", "json", "run", name) + args = append(args, "--log-format", "json", "run") + args = append(args, "--preserve-fds", "1") + args = append(args, name) + + errIn, errOut, err := os.Pipe() + if err != nil { + return err + } + errch := make(chan []byte, 1) + go func() { + errmsg, _ := ioutil.ReadAll(errIn) + errch <- errmsg + }() var cmdOut bytes.Buffer cmd := exec.Command("runc", args...) @@ -245,14 +259,23 @@ func RunInitializer(ctx context.Context, destination string, initializer *csapi. cmd.Stdout = &cmdOut cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin + cmd.ExtraFiles = []*os.File{errOut} err = cmd.Run() log.FromBuffer(&cmdOut, log.WithFields(opts.OWI.Fields())) + errOut.Close() + + var errmsg []byte + select { + case errmsg = <-errch: + case <-time.After(1 * time.Second): + errmsg = []byte("failed to read content initializer response") + } if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { // The program has exited with an exit code != 0. If it's 42, it was deliberate. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok && status.ExitStatus() == 42 { log.WithError(err).WithField("exitCode", status.ExitStatus()).WithField("args", args).Error("content init failed") - return xerrors.Errorf("content initializer failed") + return xerrors.Errorf(string(errmsg)) } } diff --git a/components/ws-daemon/pkg/content/service.go b/components/ws-daemon/pkg/content/service.go index e151d6884c71f5..67db25ee314210 100644 --- a/components/ws-daemon/pkg/content/service.go +++ b/components/ws-daemon/pkg/content/service.go @@ -235,7 +235,7 @@ func (s *WorkspaceService) InitWorkspace(ctx context.Context, req *api.InitWorks err = RunInitializer(ctx, workspace.Location, req.Initializer, remoteContent, opts) if err != nil { log.WithError(err).WithField("workspaceId", req.Id).Error("cannot initialize workspace") - return nil, status.Error(codes.Internal, fmt.Sprintf("cannot initialize workspace: %s", err.Error())) + return nil, status.Error(codes.FailedPrecondition, err.Error()) } } @@ -243,7 +243,7 @@ func (s *WorkspaceService) InitWorkspace(ctx context.Context, req *api.InitWorks err = workspace.MarkInitDone(ctx) if err != nil { log.WithError(err).WithField("workspaceId", req.Id).Error("cannot initialize workspace") - return nil, status.Error(codes.Internal, fmt.Sprintf("cannot finish workspace init: %v", err)) + return nil, status.Error(codes.FailedPrecondition, fmt.Sprintf("cannot finish workspace init: %v", err)) } return &api.InitWorkspaceResponse{}, nil