mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00
shell: decouple terminal with grpc via stream interface
This commit is contained in:
parent
8dd368a655
commit
9fe356feb5
273
cmd/go-judge-shell/grpc.go
Normal file
273
cmd/go-judge-shell/grpc.go
Normal file
@ -0,0 +1,273 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/criyle/go-judge/cmd/go-judge/model"
|
||||
"github.com/criyle/go-judge/cmd/go-judge/stream"
|
||||
"github.com/criyle/go-judge/pb"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
var _ Stream = &grpcWrapper{}
|
||||
|
||||
type grpcWrapper struct {
|
||||
sc pb.Executor_ExecStreamClient
|
||||
}
|
||||
|
||||
func newGrpc(args []string, srvAddr *string) Stream {
|
||||
token := os.Getenv("TOKEN")
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
|
||||
if token != "" {
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(newTokenAuth(token)))
|
||||
}
|
||||
conn, err := grpc.Dial(*srvAddr, opts...)
|
||||
if err != nil {
|
||||
log.Fatalln("client", err)
|
||||
}
|
||||
client := pb.NewExecutorClient(conn)
|
||||
sc, err := client.ExecStream(context.TODO())
|
||||
if err != nil {
|
||||
log.Fatalln("exec_stream", err)
|
||||
}
|
||||
log.Println("start", args)
|
||||
return &grpcWrapper{sc: sc}
|
||||
}
|
||||
|
||||
func (w *grpcWrapper) Send(req *stream.Request) error {
|
||||
switch {
|
||||
case req.Request != nil:
|
||||
w.sc.Send(convertPBRequest(req.Request))
|
||||
case req.Input != nil:
|
||||
w.sc.Send(&pb.StreamRequest{Request: &pb.StreamRequest_ExecInput{ExecInput: &pb.StreamRequest_Input{
|
||||
Name: req.Input.Name,
|
||||
Content: req.Input.Content,
|
||||
}}})
|
||||
case req.Resize != nil:
|
||||
w.sc.Send(&pb.StreamRequest{Request: &pb.StreamRequest_ExecResize{ExecResize: &pb.StreamRequest_Resize{
|
||||
Name: req.Resize.Name,
|
||||
Rows: uint32(req.Resize.Rows),
|
||||
Cols: uint32(req.Resize.Cols),
|
||||
X: uint32(req.Resize.X),
|
||||
Y: uint32(req.Resize.Y),
|
||||
}}})
|
||||
case req.Cancel != nil:
|
||||
w.sc.Send(&pb.StreamRequest{Request: &pb.StreamRequest_ExecCancel{}})
|
||||
default:
|
||||
return errors.New("send: unknown operation")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *grpcWrapper) Recv() (*stream.Response, error) {
|
||||
resp, err := w.sc.Recv()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch i := resp.Response.(type) {
|
||||
case *pb.StreamResponse_ExecOutput:
|
||||
return &stream.Response{Output: &stream.OutputResponse{
|
||||
Name: i.ExecOutput.Name,
|
||||
Content: i.ExecOutput.Content,
|
||||
}}, nil
|
||||
case *pb.StreamResponse_ExecResponse:
|
||||
return &stream.Response{Response: &model.Response{
|
||||
RequestID: i.ExecResponse.RequestID,
|
||||
Results: convertPBResult(i.ExecResponse.Results),
|
||||
ErrorMsg: i.ExecResponse.Error,
|
||||
}}, nil
|
||||
}
|
||||
return nil, errors.New("recv: invalid response")
|
||||
}
|
||||
|
||||
func convertPBResult(res []*pb.Response_Result) []model.Result {
|
||||
var ret []model.Result
|
||||
for _, r := range res {
|
||||
ret = append(ret, model.Result{
|
||||
Status: model.Status(r.Status),
|
||||
ExitStatus: int(r.ExitStatus),
|
||||
Error: r.Error,
|
||||
Time: r.Time,
|
||||
RunTime: r.RunTime,
|
||||
Memory: r.Memory,
|
||||
Files: convertFiles(r.Files),
|
||||
Buffs: r.Files,
|
||||
FileIDs: r.FileIDs,
|
||||
FileError: convertPBFileError(r.FileError),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertFiles(buf map[string][]byte) map[string]string {
|
||||
ret := make(map[string]string, len(buf))
|
||||
for k, v := range buf {
|
||||
ret[k] = byteArrayToString(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBRequest(req *model.Request) *pb.StreamRequest {
|
||||
ret := &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecRequest{
|
||||
ExecRequest: &pb.Request{
|
||||
RequestID: req.RequestID,
|
||||
Cmd: convertPBCmd(req.Cmd),
|
||||
PipeMapping: convertPBPipeMapping(req.PipeMapping),
|
||||
},
|
||||
},
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBFileError(fe []*pb.Response_FileError) []model.FileError {
|
||||
var ret []model.FileError
|
||||
for _, v := range fe {
|
||||
ret = append(ret, model.FileError{
|
||||
Name: v.Name,
|
||||
Type: model.FileErrorType(v.Type),
|
||||
Message: v.Message,
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBCmd(cmd []model.Cmd) []*pb.Request_CmdType {
|
||||
var ret []*pb.Request_CmdType
|
||||
for _, c := range cmd {
|
||||
ret = append(ret, &pb.Request_CmdType{
|
||||
Args: c.Args,
|
||||
Env: c.Env,
|
||||
Tty: c.TTY,
|
||||
Files: convertPBFiles(c.Files),
|
||||
CpuTimeLimit: c.CPULimit,
|
||||
ClockTimeLimit: c.ClockLimit,
|
||||
MemoryLimit: c.MemoryLimit,
|
||||
StackLimit: c.StackLimit,
|
||||
ProcLimit: c.ProcLimit,
|
||||
CpuRateLimit: c.CPURateLimit,
|
||||
CpuSetLimit: c.CPUSetLimit,
|
||||
DataSegmentLimit: c.DataSegmentLimit,
|
||||
AddressSpaceLimit: c.AddressSpaceLimit,
|
||||
CopyIn: convertPBCopyIn(c.CopyIn),
|
||||
CopyOut: convertPBCopyOut(c.CopyOut),
|
||||
CopyOutCached: convertPBCopyOut(c.CopyOutCached),
|
||||
CopyOutMax: c.CopyOutMax,
|
||||
CopyOutDir: c.CopyOutDir,
|
||||
Symlinks: convertSymlink(c.CopyIn),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBCopyIn(copyIn map[string]model.CmdFile) map[string]*pb.Request_File {
|
||||
rt := make(map[string]*pb.Request_File, len(copyIn))
|
||||
for k, i := range copyIn {
|
||||
if i.Symlink != nil {
|
||||
continue
|
||||
}
|
||||
rt[k] = convertPBFile(i)
|
||||
}
|
||||
return rt
|
||||
}
|
||||
|
||||
func convertPBCopyOut(copyOut []string) []*pb.Request_CmdCopyOutFile {
|
||||
rt := make([]*pb.Request_CmdCopyOutFile, len(copyOut))
|
||||
for _, n := range copyOut {
|
||||
optional := false
|
||||
if strings.HasSuffix(n, "?") {
|
||||
optional = true
|
||||
n = strings.TrimSuffix(n, "?")
|
||||
}
|
||||
rt = append(rt, &pb.Request_CmdCopyOutFile{
|
||||
Name: n,
|
||||
Optional: optional,
|
||||
})
|
||||
}
|
||||
return rt
|
||||
}
|
||||
|
||||
func convertSymlink(copyIn map[string]model.CmdFile) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
for k, v := range copyIn {
|
||||
if v.Symlink == nil {
|
||||
continue
|
||||
}
|
||||
ret[k] = *v.Symlink
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBFiles(files []*model.CmdFile) []*pb.Request_File {
|
||||
var ret []*pb.Request_File
|
||||
for _, f := range files {
|
||||
if f == nil {
|
||||
ret = append(ret, nil)
|
||||
} else {
|
||||
ret = append(ret, convertPBFile(*f))
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBFile(i model.CmdFile) *pb.Request_File {
|
||||
switch {
|
||||
case i.Src != nil:
|
||||
return &pb.Request_File{File: &pb.Request_File_Local{Local: &pb.Request_LocalFile{Src: *i.Src}}}
|
||||
case i.Content != nil:
|
||||
s := strToBytes(*i.Content)
|
||||
return &pb.Request_File{File: &pb.Request_File_Memory{Memory: &pb.Request_MemoryFile{Content: s}}}
|
||||
case i.FileID != nil:
|
||||
return &pb.Request_File{File: &pb.Request_File_Cached{Cached: &pb.Request_CachedFile{FileID: *i.FileID}}}
|
||||
case i.Name != nil && i.Max != nil:
|
||||
return &pb.Request_File{File: &pb.Request_File_Pipe{Pipe: &pb.Request_PipeCollector{Name: *i.Name, Max: *i.Max, Pipe: i.Pipe}}}
|
||||
case i.StreamIn != nil:
|
||||
return &pb.Request_File{File: &pb.Request_File_StreamIn{StreamIn: &pb.Request_StreamInput{Name: *i.StreamIn}}}
|
||||
case i.StreamOut != nil:
|
||||
return &pb.Request_File{File: &pb.Request_File_StreamOut{StreamOut: &pb.Request_StreamOutput{Name: *i.StreamOut}}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertPBPipeMapping(pm []model.PipeMap) []*pb.Request_PipeMap {
|
||||
var ret []*pb.Request_PipeMap
|
||||
for _, p := range pm {
|
||||
ret = append(ret, &pb.Request_PipeMap{
|
||||
In: convertPBPipeIndex(p.In),
|
||||
Out: convertPBPipeIndex(p.Out),
|
||||
Name: p.Name,
|
||||
Proxy: p.Proxy,
|
||||
Max: uint64(p.Max),
|
||||
})
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBPipeIndex(pi model.PipeIndex) *pb.Request_PipeMap_PipeIndex {
|
||||
return &pb.Request_PipeMap_PipeIndex{Index: int32(pi.Index), Fd: int32(pi.Fd)}
|
||||
}
|
||||
|
||||
type tokenAuth struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func newTokenAuth(token string) credentials.PerRPCCredentials {
|
||||
return &tokenAuth{token: token}
|
||||
}
|
||||
|
||||
// Return value is mapped to request headers.
|
||||
func (t *tokenAuth) GetRequestMetadata(ctx context.Context, in ...string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
"authorization": "Bearer " + t.token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*tokenAuth) RequireTransportSecurity() bool {
|
||||
return false
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -11,11 +10,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/criyle/go-judge/pb"
|
||||
"github.com/criyle/go-judge/cmd/go-judge/model"
|
||||
"github.com/criyle/go-judge/cmd/go-judge/stream"
|
||||
"golang.org/x/term"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -36,75 +33,46 @@ var env = []string{
|
||||
"TERM=" + os.Getenv("TERM"),
|
||||
}
|
||||
|
||||
type Stream interface {
|
||||
Send(*stream.Request) error
|
||||
Recv() (*stream.Response, error)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
args = []string{"/bin/bash"}
|
||||
}
|
||||
|
||||
token := os.Getenv("TOKEN")
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
|
||||
if token != "" {
|
||||
opts = append(opts, grpc.WithPerRPCCredentials(newTokenAuth(token)))
|
||||
}
|
||||
conn, err := grpc.Dial(*srvAddr, opts...)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalln("client", err)
|
||||
}
|
||||
client := pb.NewExecutorClient(conn)
|
||||
sc, err := client.ExecStream(context.TODO())
|
||||
if err != nil {
|
||||
log.Fatalln("ExecStream", err)
|
||||
}
|
||||
log.Println("Starts", args)
|
||||
r, err := run(sc, args)
|
||||
log.Println("ExecStream Finished", r, err)
|
||||
w := newGrpc(args, srvAddr)
|
||||
r, err := run(w, args)
|
||||
log.Printf("finished: %+v %v", r, err)
|
||||
}
|
||||
|
||||
func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
req := &pb.Request{
|
||||
Cmd: []*pb.Request_CmdType{{
|
||||
func stringPointer(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func run(sc Stream, args []string) (*model.Response, error) {
|
||||
req := model.Request{
|
||||
Cmd: []model.Cmd{{
|
||||
Args: args,
|
||||
Env: env,
|
||||
Files: []*pb.Request_File{
|
||||
{
|
||||
File: &pb.Request_File_StreamIn{
|
||||
StreamIn: &pb.Request_StreamInput{
|
||||
Name: "stdin",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
File: &pb.Request_File_StreamOut{
|
||||
StreamOut: &pb.Request_StreamOutput{
|
||||
Name: "stdout",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
File: &pb.Request_File_StreamOut{
|
||||
StreamOut: &pb.Request_StreamOutput{
|
||||
Name: "stderr",
|
||||
},
|
||||
},
|
||||
},
|
||||
Files: []*model.CmdFile{
|
||||
{StreamIn: stringPointer("stdin")},
|
||||
{StreamOut: stringPointer("stdout")},
|
||||
{StreamOut: stringPointer("stderr")},
|
||||
},
|
||||
CpuTimeLimit: uint64(cpuLimit),
|
||||
ClockTimeLimit: uint64(sessionLimit),
|
||||
MemoryLimit: memoryLimit,
|
||||
ProcLimit: procLimit,
|
||||
Tty: true,
|
||||
CPULimit: uint64(cpuLimit.Nanoseconds()),
|
||||
ClockLimit: uint64(sessionLimit.Nanoseconds()),
|
||||
MemoryLimit: memoryLimit,
|
||||
ProcLimit: procLimit,
|
||||
TTY: true,
|
||||
}},
|
||||
}
|
||||
err := sc.Send(&pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecRequest{
|
||||
ExecRequest: req,
|
||||
},
|
||||
})
|
||||
err := sc.Send(&stream.Request{Request: &req})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ExecStream Send request %v", err)
|
||||
return nil, fmt.Errorf("send request: %w", err)
|
||||
}
|
||||
|
||||
// Set stdin in raw mode.
|
||||
@ -115,7 +83,7 @@ func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
defer func() { _ = term.Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||
|
||||
// pump msg
|
||||
sendCh := make(chan *pb.StreamRequest, 64)
|
||||
sendCh := make(chan *stream.Request, 64)
|
||||
defer close(sendCh)
|
||||
go func() {
|
||||
for r := range sendCh {
|
||||
@ -134,20 +102,18 @@ func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
for {
|
||||
n, err := os.Stdin.Read(buf)
|
||||
if err == io.EOF {
|
||||
sendCh <- &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecInput{
|
||||
ExecInput: &pb.StreamRequest_Input{
|
||||
Name: "stdin",
|
||||
Content: []byte("\004"),
|
||||
},
|
||||
sendCh <- &stream.Request{
|
||||
Input: &stream.InputRequest{
|
||||
Name: "stdin",
|
||||
Content: []byte("\004"),
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
if n == 1 && buf[0] == 3 {
|
||||
if forceQuit {
|
||||
sendCh <- &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecCancel{},
|
||||
sendCh <- &stream.Request{
|
||||
Cancel: &struct{}{},
|
||||
}
|
||||
}
|
||||
forceQuit = true
|
||||
@ -159,12 +125,10 @@ func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
log.Println("stdin", err)
|
||||
return
|
||||
}
|
||||
sendCh <- &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecInput{
|
||||
ExecInput: &pb.StreamRequest_Input{
|
||||
Name: "stdin",
|
||||
Content: buf[:n],
|
||||
},
|
||||
sendCh <- &stream.Request{
|
||||
Input: &stream.InputRequest{
|
||||
Name: "stdin",
|
||||
Content: buf[:n],
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -175,12 +139,10 @@ func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
// pump ^C
|
||||
go func() {
|
||||
for range sigCh {
|
||||
sendCh <- &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecInput{
|
||||
ExecInput: &pb.StreamRequest_Input{
|
||||
Name: "stdin",
|
||||
Content: []byte("\003"),
|
||||
},
|
||||
sendCh <- &stream.Request{
|
||||
Input: &stream.InputRequest{
|
||||
Name: "stdin",
|
||||
Content: []byte("\003"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -193,37 +155,18 @@ func run(sc pb.Executor_ExecStreamClient, args []string) (*pb.Response, error) {
|
||||
for {
|
||||
sr, err := sc.Recv()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ExecStream recv %v", err)
|
||||
return nil, fmt.Errorf("recv: %w", err)
|
||||
}
|
||||
switch sr := sr.Response.(type) {
|
||||
case *pb.StreamResponse_ExecOutput:
|
||||
switch sr.ExecOutput.Name {
|
||||
switch {
|
||||
case sr.Output != nil:
|
||||
switch sr.Output.Name {
|
||||
case "stdout":
|
||||
os.Stdout.Write(sr.ExecOutput.Content)
|
||||
os.Stdout.Write(sr.Output.Content)
|
||||
case "stderr":
|
||||
os.Stderr.Write(sr.ExecOutput.Content)
|
||||
os.Stderr.Write(sr.Output.Content)
|
||||
}
|
||||
case *pb.StreamResponse_ExecResponse:
|
||||
return sr.ExecResponse, nil
|
||||
case sr.Response != nil:
|
||||
return sr.Response, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type tokenAuth struct {
|
||||
token string
|
||||
}
|
||||
|
||||
func newTokenAuth(token string) credentials.PerRPCCredentials {
|
||||
return &tokenAuth{token: token}
|
||||
}
|
||||
|
||||
// Return value is mapped to request headers.
|
||||
func (t *tokenAuth) GetRequestMetadata(ctx context.Context, in ...string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
"authorization": "Bearer " + t.token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*tokenAuth) RequireTransportSecurity() bool {
|
||||
return false
|
||||
}
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/criyle/go-judge/pb"
|
||||
"github.com/criyle/go-judge/cmd/go-judge/stream"
|
||||
)
|
||||
|
||||
func handleSizeChange(sendCh chan<- *pb.StreamRequest) {
|
||||
func handleSizeChange(sendCh chan *stream.Request) {
|
||||
// pump resize
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGWINCH)
|
||||
@ -21,15 +21,13 @@ func handleSizeChange(sendCh chan<- *pb.StreamRequest) {
|
||||
log.Println("get win size", err)
|
||||
return
|
||||
}
|
||||
sendCh <- &pb.StreamRequest{
|
||||
Request: &pb.StreamRequest_ExecResize{
|
||||
ExecResize: &pb.StreamRequest_Resize{
|
||||
Name: "stdin",
|
||||
Rows: uint32(winSize.Rows),
|
||||
Cols: uint32(winSize.Cols),
|
||||
X: uint32(winSize.X),
|
||||
Y: uint32(winSize.Y),
|
||||
},
|
||||
sendCh <- &stream.Request{
|
||||
Resize: &stream.ResizeRequest{
|
||||
Name: "stdin",
|
||||
Rows: int(winSize.Rows),
|
||||
Cols: int(winSize.Cols),
|
||||
X: int(winSize.X),
|
||||
Y: int(winSize.Y),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/criyle/go-judge/pb"
|
||||
import "github.com/criyle/go-judge/cmd/go-judge/stream"
|
||||
|
||||
func handleSizeChange(sendCh chan<- *pb.StreamRequest) {
|
||||
func handleSizeChange(sendCh chan *stream.Request) {
|
||||
}
|
||||
|
11
cmd/go-judge-shell/util.go
Normal file
11
cmd/go-judge-shell/util.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func strToBytes(s string) []byte {
|
||||
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||
}
|
||||
|
||||
func byteArrayToString(buf []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&buf))
|
||||
}
|
@ -176,20 +176,18 @@ func convertPBRequest(r *pb.Request, srcPrefix []string) (req *worker.Request, e
|
||||
|
||||
func convertPBPipeMap(p *pb.Request_PipeMap) worker.PipeMap {
|
||||
return worker.PipeMap{
|
||||
In: worker.PipeIndex{
|
||||
Index: int(p.GetIn().GetIndex()),
|
||||
Fd: int(p.GetIn().GetFd()),
|
||||
},
|
||||
Out: worker.PipeIndex{
|
||||
Index: int(p.GetOut().GetIndex()),
|
||||
Fd: int(p.GetOut().GetFd()),
|
||||
},
|
||||
In: convertPBPipeIndex(p.GetIn()),
|
||||
Out: convertPBPipeIndex(p.GetOut()),
|
||||
Proxy: p.GetProxy(),
|
||||
Name: p.GetName(),
|
||||
Limit: worker.Size(p.Max),
|
||||
}
|
||||
}
|
||||
|
||||
func convertPBPipeIndex(p *pb.Request_PipeMap_PipeIndex) worker.PipeIndex {
|
||||
return worker.PipeIndex{Index: int(p.GetIndex()), Fd: int(p.GetFd())}
|
||||
}
|
||||
|
||||
func convertPBCmd(c *pb.Request_CmdType, srcPrefix []string) (cm worker.Cmd, err error) {
|
||||
cm = worker.Cmd{
|
||||
Args: c.GetArgs(),
|
||||
|
@ -89,14 +89,8 @@ func convertPBStreamRequest(req *pb.Request) *model.Request {
|
||||
}
|
||||
for _, p := range req.PipeMapping {
|
||||
ret.PipeMapping = append(ret.PipeMapping, model.PipeMap{
|
||||
In: model.PipeIndex{
|
||||
Index: int(p.In.Index),
|
||||
Fd: int(p.In.Fd),
|
||||
},
|
||||
Out: model.PipeIndex{
|
||||
Index: int(p.Out.Index),
|
||||
Fd: int(p.Out.Fd),
|
||||
},
|
||||
In: convertPBStreamPipeIndex(p.In),
|
||||
Out: convertPBStreamPipeIndex(p.Out),
|
||||
Max: int64(p.Max),
|
||||
Name: p.Name,
|
||||
Proxy: p.Proxy,
|
||||
@ -105,6 +99,10 @@ func convertPBStreamRequest(req *pb.Request) *model.Request {
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertPBStreamPipeIndex(pi *pb.Request_PipeMap_PipeIndex) model.PipeIndex {
|
||||
return model.PipeIndex{Index: int(pi.Index), Fd: int(pi.Fd)}
|
||||
}
|
||||
|
||||
func convertPBStreamFiles(files []*pb.Request_File) []*model.CmdFile {
|
||||
var rt []*model.CmdFile
|
||||
for _, f := range files {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -11,6 +12,9 @@ import (
|
||||
"github.com/criyle/go-judge/worker"
|
||||
)
|
||||
|
||||
type FileError = envexec.FileError
|
||||
type FileErrorType = envexec.FileErrorType
|
||||
|
||||
// CmdFile defines file from multiple source including local / memory / cached or pipe collector
|
||||
type CmdFile struct {
|
||||
Src *string `json:"src"`
|
||||
@ -77,9 +81,14 @@ type Request struct {
|
||||
// Status offers JSON marshal for envexec.Status
|
||||
type Status envexec.Status
|
||||
|
||||
// String converts status to string
|
||||
func (s Status) String() string {
|
||||
return envexec.Status(s).String()
|
||||
}
|
||||
|
||||
// MarshalJSON convert status into string
|
||||
func (s Status) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"" + (envexec.Status)(s).String() + "\""), nil
|
||||
return []byte("\"" + envexec.Status(s).String() + "\""), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON convert string into status
|
||||
@ -95,20 +104,49 @@ func (s *Status) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// Result defines single command result
|
||||
type Result struct {
|
||||
Status Status `json:"status"`
|
||||
ExitStatus int `json:"exitStatus"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Time uint64 `json:"time"`
|
||||
Memory uint64 `json:"memory"`
|
||||
RunTime uint64 `json:"runTime"`
|
||||
Files map[string]string `json:"files,omitempty"`
|
||||
FileIDs map[string]string `json:"fileIds,omitempty"`
|
||||
FileError []envexec.FileError `json:"fileError,omitempty"`
|
||||
Status Status `json:"status"`
|
||||
ExitStatus int `json:"exitStatus"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Time uint64 `json:"time"`
|
||||
Memory uint64 `json:"memory"`
|
||||
RunTime uint64 `json:"runTime"`
|
||||
Files map[string]string `json:"files,omitempty"`
|
||||
FileIDs map[string]string `json:"fileIds,omitempty"`
|
||||
FileError []FileError `json:"fileError,omitempty"`
|
||||
|
||||
files []string
|
||||
Buffs map[string][]byte `json:"-"`
|
||||
}
|
||||
|
||||
func (r Result) String() string {
|
||||
type Result struct {
|
||||
Status Status
|
||||
ExitStatus int
|
||||
Error string
|
||||
Time time.Duration
|
||||
RunTime time.Duration
|
||||
Memory envexec.Size
|
||||
Files map[string]string
|
||||
FileIDs map[string]string
|
||||
FileError []FileError
|
||||
}
|
||||
d := Result{
|
||||
Status: r.Status,
|
||||
ExitStatus: r.ExitStatus,
|
||||
Error: r.Error,
|
||||
Time: time.Duration(r.Time),
|
||||
RunTime: time.Duration(r.RunTime),
|
||||
Memory: envexec.Size(r.Memory),
|
||||
Files: make(map[string]string),
|
||||
FileIDs: r.FileIDs,
|
||||
FileError: r.FileError,
|
||||
}
|
||||
for k, v := range r.Files {
|
||||
d.Files[k] = "len:" + strconv.Itoa(len(v))
|
||||
}
|
||||
return fmt.Sprintf("%+v", d)
|
||||
}
|
||||
|
||||
// Response defines worker response for single request
|
||||
type Response struct {
|
||||
RequestID string `json:"requestId"`
|
||||
|
@ -13,6 +13,7 @@ type Size = envexec.Size
|
||||
type CmdCopyOutFile = envexec.CmdCopyOutFile
|
||||
type PipeMap = envexec.Pipe
|
||||
type PipeIndex = envexec.PipeIndex
|
||||
type FileError = envexec.FileError
|
||||
|
||||
// Cmd defines command and limits to start a program using in envexec
|
||||
type Cmd struct {
|
||||
@ -57,10 +58,10 @@ type Result struct {
|
||||
Error string
|
||||
Time time.Duration
|
||||
RunTime time.Duration
|
||||
Memory envexec.Size
|
||||
Memory Size
|
||||
Files map[string]*os.File
|
||||
FileIDs map[string]string
|
||||
FileError []envexec.FileError
|
||||
FileError []FileError
|
||||
}
|
||||
|
||||
// Response defines worker response for single request
|
||||
@ -77,10 +78,10 @@ func (r Result) String() string {
|
||||
Error string
|
||||
Time time.Duration
|
||||
RunTime time.Duration
|
||||
Memory envexec.Size
|
||||
Memory Size
|
||||
Files map[string]string
|
||||
FileIDs map[string]string
|
||||
FileError []envexec.FileError
|
||||
FileError []FileError
|
||||
}
|
||||
d := Result{
|
||||
Status: r.Status,
|
||||
|
Loading…
Reference in New Issue
Block a user