зеркало из https://github.com/golang/playground.git
playground: add sandbox and nacl base image
Change-Id: If6c2893ef8df63a3005ec85075db2e9df0cbf993
This commit is contained in:
Родитель
dba9ba1953
Коммит
b0bb4c447c
4
README
4
README
|
@ -1,4 +0,0 @@
|
|||
This subrepository holds the source for various packages and tools that support
|
||||
the Go playground: https://play.golang.org/
|
||||
|
||||
To submit changes to this repository, see http://golang.org/doc/contribute.html.
|
|
@ -0,0 +1,26 @@
|
|||
# playground
|
||||
|
||||
This subrepository holds the source for various packages and tools that support
|
||||
the Go playground: https://play.golang.org/
|
||||
|
||||
## building
|
||||
|
||||
```
|
||||
# build the golang:nacl base image
|
||||
docker build -t golang:nacl gonacl/
|
||||
# build the sandbox server image
|
||||
docker build -t sandbox sandbox/
|
||||
```
|
||||
|
||||
## running
|
||||
|
||||
```
|
||||
# run the sandbox
|
||||
docker run -d -p 8080:8080 sandbox
|
||||
# get docker host ip, try boot2docker fallback on localhost.
|
||||
DOCKER_HOST_IP=$(boot2docker ip || echo localhost)
|
||||
# run go some code
|
||||
cat /path/to/code.go | go run ./sandbox/client.go | curl --data @- $DOCKER_HOST_IP:8080/compile
|
||||
```
|
||||
|
||||
To submit changes to this repository, see http://golang.org/doc/contribute.html.
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2014 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
FROM golang:1.4
|
||||
|
||||
# enable faketime
|
||||
RUN apt-get update && apt-get install -yq --no-install-recommends patch
|
||||
ADD enable-fake-time.patch /usr/src/go/
|
||||
RUN patch /usr/src/go/src/runtime/rt0_nacl_amd64p32.s /usr/src/go/enable-fake-time.patch
|
||||
|
||||
# build go nacl tool chain
|
||||
RUN cd /usr/src/go/src && GOOS=nacl GOARCH=amd64p32 ./make.bash --no-clean
|
||||
RUN cd /usr/local/bin && curl -s -O https://storage.googleapis.com/gobuilder/sel_ldr_x86_64 && chmod +x sel_ldr_x86_64
|
||||
|
||||
# add and compile sandbox daemon
|
||||
ADD . /go/src/sandbox/
|
||||
RUN go install sandbox
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/go/bin/sandbox"]
|
|
@ -0,0 +1,26 @@
|
|||
// +build ignore
|
||||
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
body, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalf("error reading stdin: %v", err)
|
||||
}
|
||||
json.NewEncoder(os.Stdout).Encode(struct {
|
||||
Body string
|
||||
}{
|
||||
Body: string(body),
|
||||
})
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
--- rt0_nacl_amd64p32.s 2014-10-28 17:28:25.028188222 -0700
|
||||
+++ rt0_nacl_amd64p32-faketime.s 2014-10-28 17:28:06.363674896 -0700
|
||||
@@ -25,6 +25,6 @@
|
||||
|
||||
TEXT main(SB),NOSPLIT,$0
|
||||
// Uncomment for fake time like on Go Playground.
|
||||
- //MOVQ $1257894000000000000, AX
|
||||
- //MOVQ AX, runtime·faketime(SB)
|
||||
+ MOVQ $1257894000000000000, AX
|
||||
+ MOVQ AX, runtime·faketime(SB)
|
||||
JMP runtime·rt0_go(SB)
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Message string
|
||||
Delay time.Duration // time to wait before printing Message
|
||||
Kind string
|
||||
}
|
||||
|
||||
// Decode takes an output string comprised of playback headers, and converts
|
||||
// it to a sequence of Events.
|
||||
// It sanitizes each Event's Message to ensure it is valid UTF-8.
|
||||
//
|
||||
// Playground programs precede all their writes with a header (described
|
||||
// below) that describes the time the write occurred (in playground time) and
|
||||
// the length of the data that will be written. If a non-header is
|
||||
// encountered where a header is expected, the output is scanned for the next
|
||||
// header and the intervening text string is added to the sequence an event
|
||||
// occurring at the same time as the preceding event.
|
||||
//
|
||||
// A playback header has this structure:
|
||||
// 4 bytes: "\x00\x00PB", a magic header
|
||||
// 8 bytes: big-endian int64, unix time in nanoseconds
|
||||
// 4 bytes: big-endian int32, length of the next write
|
||||
//
|
||||
func Decode(output []byte) (seq []Event, err error) {
|
||||
var (
|
||||
magic = []byte{0, 0, 'P', 'B'}
|
||||
headerLen = 8 + 4
|
||||
now = time.Unix(1257894000, 0) // go epoch
|
||||
)
|
||||
|
||||
add := func(t time.Time, b []byte) {
|
||||
e := Event{
|
||||
Message: string(sanitize(b)),
|
||||
Delay: t.Sub(now),
|
||||
}
|
||||
if e.Delay == 0 && len(seq) > 0 {
|
||||
// Merge this event with previous event, to avoid
|
||||
// sending a lot of events for a big output with no
|
||||
// significant timing information.
|
||||
seq[len(seq)-1].Message += e.Message
|
||||
} else {
|
||||
seq = append(seq, e)
|
||||
}
|
||||
now = t
|
||||
}
|
||||
|
||||
for i := 0; i < len(output); {
|
||||
if !bytes.HasPrefix(output[i:], magic) {
|
||||
// Not a header; find next header.
|
||||
j := bytes.Index(output[i:], magic)
|
||||
if j < 0 {
|
||||
// No more headers; bail.
|
||||
add(now, output[i:])
|
||||
break
|
||||
}
|
||||
add(now, output[i:i+j])
|
||||
i += j
|
||||
}
|
||||
i += len(magic)
|
||||
|
||||
// Decode header.
|
||||
if len(output)-i < headerLen {
|
||||
return nil, errors.New("short header")
|
||||
}
|
||||
header := output[i : i+headerLen]
|
||||
nanos := int64(binary.BigEndian.Uint64(header[0:]))
|
||||
t := time.Unix(0, nanos)
|
||||
if t.Before(now) {
|
||||
// Force timestamps to be monotonic. (This could
|
||||
// be an encoding error, which we ignore now but will
|
||||
// will likely be picked up when decoding the length.)
|
||||
t = now
|
||||
}
|
||||
n := int(binary.BigEndian.Uint32(header[8:]))
|
||||
if n < 0 {
|
||||
return nil, fmt.Errorf("bad length: %v", n)
|
||||
}
|
||||
i += headerLen
|
||||
|
||||
// Slurp output.
|
||||
// Truncated output is OK (probably caused by sandbox limits).
|
||||
end := i + n
|
||||
if end > len(output) {
|
||||
end = len(output)
|
||||
}
|
||||
add(t, output[i:end])
|
||||
i += n
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sanitize scans b for invalid utf8 code points. If found, it reconstructs
|
||||
// the slice replacing the invalid codes with \uFFFD, properly encoded.
|
||||
func sanitize(b []byte) []byte {
|
||||
if utf8.Valid(b) {
|
||||
return b
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
b = b[size:]
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Command sandbox is an HTTP server that takes requests containing go
|
||||
// source files, and builds and executes them in a NaCl sanbox.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Body string
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Errors string
|
||||
Events []Event
|
||||
}
|
||||
|
||||
func handleCompile(w http.ResponseWriter, r *http.Request) {
|
||||
var codeRequest Request
|
||||
if err := json.NewDecoder(r.Body).Decode(&codeRequest); err != nil {
|
||||
http.Error(w, fmt.Sprintf("request error: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
resp, err := compileAndRun(&codeRequest)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("sandbox error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
http.Error(w, fmt.Sprintf("response error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func compileAndRun(req *Request) (*Response, error) {
|
||||
tmpDir, err := ioutil.TempDir("", "sandbox")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating temp directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
in := path.Join(tmpDir, "main.go")
|
||||
if err := ioutil.WriteFile(in, []byte(req.Body), 0400); err != nil {
|
||||
return nil, fmt.Errorf("error creating temp file %q: %v", in, err)
|
||||
}
|
||||
exe := path.Join(tmpDir, "a.out")
|
||||
cmd := exec.Command("go", "build", "-o", exe, in)
|
||||
cmd.Env = []string{
|
||||
"GOOS=nacl",
|
||||
"GOARCH=amd64p32",
|
||||
}
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
// build error
|
||||
return &Response{
|
||||
Errors: string(out),
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("error building go source: %v", err)
|
||||
}
|
||||
// TODO(proppy): restrict run time and memory use.
|
||||
cmd = exec.Command("sel_ldr_x86_64", "-l", "/dev/null", "-S", "-e", exe)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok {
|
||||
return nil, fmt.Errorf("error running sandbox: %v", err)
|
||||
}
|
||||
}
|
||||
events, err := Decode(out)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding events: %v", err)
|
||||
}
|
||||
return &Response{
|
||||
Events: events,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/compile", handleCompile)
|
||||
http.HandleFunc("/_ah/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, "ok")
|
||||
})
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
Загрузка…
Ссылка в новой задаче