Merge sandbox and frontend into one service

Since frontend was moved to the flex runtime, there is no longer
a reason to keep the two separate. Consolidate the two into one
service to reduce deployment complexity.

Change-Id: Ie64c17e1833ed94ac8bff7381963f683d824ca5d
Reviewed-on: https://go-review.googlesource.com/85456
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 88dff59..b252820 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,7 +24,7 @@
 before sending patches.
 
 **We do not accept GitHub pull requests**
-(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+(we use [Gerrit](https://www.gerritcodereview.com/) instead for code review).
 
 Unless otherwise noted, the Go source files are distributed under
 the BSD-style license found in the LICENSE file.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..a99db1e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,181 @@
+# Copyright 2017 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 debian:jessie
+LABEL maintainer "golang-dev@googlegroups.com"
+
+ENV GOPATH /go
+ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
+ENV GOROOT_BOOTSTRAP /usr/local/gobootstrap
+ENV GO_VERSION 1.9.2
+ENV BUILD_DEPS 'curl bzip2 git ca-certificates gcc patch libc6-dev'
+
+# Fake time
+COPY enable-fake-time.patch /usr/local/playground/
+# Fake file system
+COPY fake_fs.lst /usr/local/playground/
+
+RUN set -x && \
+    apt-get update && apt-get install -y ${BUILD_DEPS} --no-install-recommends && rm -rf /var/lib/apt/lists/*
+
+RUN curl -s https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/49.0.2623.87/naclsdk_linux.tar.bz2 | tar -xj -C /usr/local/bin --strip-components=2 pepper_49/tools/sel_ldr_x86_64
+
+# Get the Go binary.
+RUN curl -sSL https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz -o /tmp/go.tar.gz && \
+    curl -sSL https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz.sha256 -o /tmp/go.tar.gz.sha256 && \
+    echo "$(cat /tmp/go.tar.gz.sha256) /tmp/go.tar.gz" | sha256sum -c - && \
+    tar -C /usr/local/ -vxzf /tmp/go.tar.gz && \
+    rm /tmp/go.tar.gz /tmp/go.tar.gz.sha256 && \
+    # Make a copy for GOROOT_BOOTSTRAP, because we rebuild the toolchain and make.bash removes bin/go as its first step.
+    cp -R /usr/local/go $GOROOT_BOOTSTRAP && \
+    # Apply the fake time and fake filesystem patches.
+    patch /usr/local/go/src/runtime/rt0_nacl_amd64p32.s /usr/local/playground/enable-fake-time.patch && \
+    cd /usr/local/go && go run misc/nacl/mkzip.go -p syscall /usr/local/playground/fake_fs.lst src/syscall/fstest_nacl.go && \
+    # Re-build the Go toolchain.
+    cd /usr/local/go/src && GOOS=nacl GOARCH=amd64p32 ./make.bash --no-clean && \
+    # Clean up.
+    rm -rf $GOROOT_BOOTSTRAP
+
+# Add and compile tour packages
+RUN GOOS=nacl GOARCH=amd64p32 go get \
+    golang.org/x/tour/pic \
+    golang.org/x/tour/reader \
+    golang.org/x/tour/tree \
+    golang.org/x/tour/wc \
+    golang.org/x/talks/2016/applicative/google && \
+    rm -rf $GOPATH/src/golang.org/x/tour/.git && \
+    rm -rf $GOPATH/src/golang.org/x/talks/.git
+
+# Add tour packages under their old import paths (so old snippets still work)
+RUN mkdir -p $GOPATH/src/code.google.com/p/go-tour && \
+    cp -R $GOPATH/src/golang.org/x/tour/* $GOPATH/src/code.google.com/p/go-tour/ && \
+    sed -i 's_// import_// public import_' $(find $GOPATH/src/code.google.com/p/go-tour/ -name *.go) && \
+    go install \
+    code.google.com/p/go-tour/pic \
+    code.google.com/p/go-tour/reader \
+    code.google.com/p/go-tour/tree \
+    code.google.com/p/go-tour/wc
+
+# BEGIN deps (run `make update-deps` to update)
+
+# Repo cloud.google.com/go at 558b56d (2017-07-03)
+ENV REV=558b56dfa3c56acc26fef35cb07f97df0bb18b39
+RUN go get -d cloud.google.com/go/compute/metadata `#and 5 other pkgs` &&\
+    (cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo github.com/golang/protobuf at 748d386 (2017-07-26)
+ENV REV=748d386b5c1ea99658fd69fe9f03991ce86a90c1
+RUN go get -d github.com/golang/protobuf/proto `#and 6 other pkgs` &&\
+    (cd /go/src/github.com/golang/protobuf && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/net at 66aacef (2017-08-28)
+ENV REV=66aacef3dd8a676686c7ae3716979581e8b03c47
+RUN go get -d golang.org/x/net/context `#and 8 other pkgs` &&\
+    (cd /go/src/golang.org/x/net && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/oauth2 at cce311a (2017-06-29)
+ENV REV=cce311a261e6fcf29de72ca96827bdb0b7d9c9e6
+RUN go get -d golang.org/x/oauth2 `#and 5 other pkgs` &&\
+    (cd /go/src/golang.org/x/oauth2 && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/text at 2bf8f2a (2017-06-30)
+ENV REV=2bf8f2a19ec09c670e931282edfe6567f6be21c9
+RUN go get -d golang.org/x/text/secure/bidirule `#and 4 other pkgs` &&\
+    (cd /go/src/golang.org/x/text && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo golang.org/x/tools at 89c69fd (2017-09-01)
+ENV REV=89c69fd3045b723bb4d9f75d73b881c50ab481c0
+RUN go get -d golang.org/x/tools/go/ast/astutil `#and 3 other pkgs` &&\
+    (cd /go/src/golang.org/x/tools && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/api at e6586c9 (2017-06-27)
+ENV REV=e6586c9293b9d514c7f5d5076731ec977cff1be6
+RUN go get -d google.golang.org/api/googleapi/transport `#and 5 other pkgs` &&\
+    (cd /go/src/google.golang.org/api && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/genproto at aa2eb68 (2017-06-01)
+ENV REV=aa2eb687b4d3e17154372564ad8d6bf11c3cf21f
+RUN go get -d google.golang.org/genproto/googleapis/api/annotations `#and 4 other pkgs` &&\
+    (cd /go/src/google.golang.org/genproto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Repo google.golang.org/grpc at 3c33c26 (2017-06-27)
+ENV REV=3c33c26290b747350f8650c7d38bcc51b42dc785
+RUN go get -d google.golang.org/grpc `#and 15 other pkgs` &&\
+    (cd /go/src/google.golang.org/grpc && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
+
+# Optimization to speed up iterative development, not necessary for correctness:
+RUN go install cloud.google.com/go/compute/metadata \
+	cloud.google.com/go/datastore \
+	cloud.google.com/go/internal/atomiccache \
+	cloud.google.com/go/internal/fields \
+	cloud.google.com/go/internal/version \
+	github.com/golang/protobuf/proto \
+	github.com/golang/protobuf/protoc-gen-go/descriptor \
+	github.com/golang/protobuf/ptypes/any \
+	github.com/golang/protobuf/ptypes/struct \
+	github.com/golang/protobuf/ptypes/timestamp \
+	github.com/golang/protobuf/ptypes/wrappers \
+	golang.org/x/net/context \
+	golang.org/x/net/context/ctxhttp \
+	golang.org/x/net/http2 \
+	golang.org/x/net/http2/hpack \
+	golang.org/x/net/idna \
+	golang.org/x/net/internal/timeseries \
+	golang.org/x/net/lex/httplex \
+	golang.org/x/net/trace \
+	golang.org/x/oauth2 \
+	golang.org/x/oauth2/google \
+	golang.org/x/oauth2/internal \
+	golang.org/x/oauth2/jws \
+	golang.org/x/oauth2/jwt \
+	golang.org/x/text/secure/bidirule \
+	golang.org/x/text/transform \
+	golang.org/x/text/unicode/bidi \
+	golang.org/x/text/unicode/norm \
+	golang.org/x/tools/go/ast/astutil \
+	golang.org/x/tools/godoc/static \
+	golang.org/x/tools/imports \
+	google.golang.org/api/googleapi/transport \
+	google.golang.org/api/internal \
+	google.golang.org/api/iterator \
+	google.golang.org/api/option \
+	google.golang.org/api/transport \
+	google.golang.org/genproto/googleapis/api/annotations \
+	google.golang.org/genproto/googleapis/datastore/v1 \
+	google.golang.org/genproto/googleapis/rpc/status \
+	google.golang.org/genproto/googleapis/type/latlng \
+	google.golang.org/grpc \
+	google.golang.org/grpc/codes \
+	google.golang.org/grpc/credentials \
+	google.golang.org/grpc/credentials/oauth \
+	google.golang.org/grpc/grpclb/grpc_lb_v1 \
+	google.golang.org/grpc/grpclog \
+	google.golang.org/grpc/internal \
+	google.golang.org/grpc/keepalive \
+	google.golang.org/grpc/metadata \
+	google.golang.org/grpc/naming \
+	google.golang.org/grpc/peer \
+	google.golang.org/grpc/stats \
+	google.golang.org/grpc/status \
+	google.golang.org/grpc/tap \
+	google.golang.org/grpc/transport
+# END deps
+
+RUN apt-get purge -y --auto-remove ${BUILD_DEPS}
+
+# Add and compile playground daemon
+COPY . /go/src/playground/
+RUN go install playground
+
+RUN mkdir /app
+
+COPY edit.html /app
+COPY static /app/static
+
+WORKDIR /app
+
+# Run tests
+RUN /go/bin/playground test
+
+EXPOSE 8080
+ENTRYPOINT ["/go/bin/playground"]
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0854a90
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
+.PHONY: update-deps docker test
+
+update-deps:
+	go install golang.org/x/build/cmd/gitlock
+	gitlock --update=Dockerfile golang.org/x/playground/frontend
+
+docker: Dockerfile
+	docker build -t playground .
+
+test: docker
+	go test
+	docker run --rm playground test
diff --git a/README.md b/README.md
index 2fd7edc..ec92d82 100644
--- a/README.md
+++ b/README.md
@@ -1,78 +1,31 @@
 # playground
 
-This subrepository holds the source for various packages and tools that support
-the Go playground: https://play.golang.org/
+This subrepository holds the source for the Go playground:
+https://play.golang.org/
 
 To submit changes to this repository, see http://golang.org/doc/contribute.html.
 
-## Frontend
-
-### Building
+## Building
 
 ```
-# build the frontend image
-docker build -t frontend frontend/
+# build the image
+docker build -t playground .
 ```
 
-### Running with an in-memory store
+## Running
 
 ```
-docker run --rm -d -p 8080:8080 frontend
-```
-
-### Running with the Cloud Datastore Emulator
-
-```
-# install it if needed
-gcloud components install cloud-datastore-emulator
-# run the datastore emulator
-gcloud --project=golang-org beta emulators datastore start
-# set env vars
-$(gcloud beta emulators datastore env-init)
-# run the frontend
-cd frontend && go install && frontend
-```
-
-Now visit localhost:8080 to ensure it worked.
-
-```
-# unset any env vars once finished
-$(gcloud beta emulators datastore env-unset)
-```
-
-## Sandbox
-
-### Building
-
-```
-# build the sandbox 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)
+docker run --rm -d -p 8080:8080 playground
 # run go some code
-cat /path/to/code.go | go run ./sandbox/client.go | curl --data @- $DOCKER_HOST_IP:8080/compile
+cat /path/to/code.go | go run client.go | curl --data @- localhost:8080/compile
 ```
 
-To submit changes to this repository, see http://golang.org/doc/contribute.html.
-
 # Deployment
 
 ```
-gcloud --project=golang-org --account=person@example.com app deploy frontend/app.yaml
+gcloud --project=golang-org --account=person@example.com app deploy app.yaml
 ```
 
-```
-gcloud --project=golang-org --account=person@example.com app deploy sandbox/app-flex.yaml --no-promote
-```
+# Contributing
 
-Use the Cloud Console's to set the new version as the default:
-https://cloud.google.com/console/appengine/versions?project=golang-org&moduleId=sandbox-flex
-Then test that play.golang.org and tour.golang.org are working before deleting
-the old version.
+To submit changes to this repository, see http://golang.org/doc/contribute.html.
\ No newline at end of file
diff --git a/frontend/app.yaml b/app.yaml
similarity index 100%
rename from frontend/app.yaml
rename to app.yaml
diff --git a/sandbox/client.go b/client.go
similarity index 87%
rename from sandbox/client.go
rename to client.go
index eee62f9..c168c3c 100644
--- a/sandbox/client.go
+++ b/client.go
@@ -1,6 +1,6 @@
 // +build ignore
 
-// Copyright 2014 The Go Authors.  All rights reserved.
+// 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.
 
diff --git a/frontend/edit.go b/edit.go
similarity index 100%
rename from frontend/edit.go
rename to edit.go
diff --git a/frontend/edit.html b/edit.html
similarity index 100%
rename from frontend/edit.html
rename to edit.html
diff --git a/sandbox/enable-fake-time.patch b/enable-fake-time.patch
similarity index 100%
rename from sandbox/enable-fake-time.patch
rename to enable-fake-time.patch
diff --git a/sandbox/fake_fs.lst b/fake_fs.lst
similarity index 100%
rename from sandbox/fake_fs.lst
rename to fake_fs.lst
diff --git a/frontend/fmt.go b/fmt.go
similarity index 100%
rename from frontend/fmt.go
rename to fmt.go
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
deleted file mode 100644
index 1594a89..0000000
--- a/frontend/Dockerfile
+++ /dev/null
@@ -1,143 +0,0 @@
-# Copyright 2017 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.9.2
-LABEL maintainer "golang-dev@googlegroups.com"
-
-# BEGIN deps (run `make update-deps` to update)
-
-# Repo cloud.google.com/go at 3051b91 (2017-12-06)
-ENV REV=3051b919da3b8d62bc3a57ab4b353ca1c72402d5
-RUN go get -d cloud.google.com/go/compute/metadata `#and 6 other pkgs` &&\
-    (cd /go/src/cloud.google.com/go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo github.com/golang/protobuf at 1e59b77 (2017-11-13)
-ENV REV=1e59b77b52bf8e4b449a57e6f79f21226d571845
-RUN go get -d github.com/golang/protobuf/proto `#and 8 other pkgs` &&\
-    (cd /go/src/github.com/golang/protobuf && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo github.com/googleapis/gax-go at 317e000 (2017-09-15)
-ENV REV=317e0006254c44a0ac427cc52a0e083ff0b9622f
-RUN go get -d github.com/googleapis/gax-go &&\
-    (cd /go/src/github.com/googleapis/gax-go && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/net at faacc1b (2017-12-07)
-ENV REV=faacc1b5e36e3ff02cbec9661c69ac63dd5a83ad
-RUN go get -d golang.org/x/net/context `#and 8 other pkgs` &&\
-    (cd /go/src/golang.org/x/net && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/oauth2 at 6a2004c (2017-12-06)
-ENV REV=6a2004c8907a86949d71c664c81574897a4e55a6
-RUN go get -d golang.org/x/oauth2 `#and 5 other pkgs` &&\
-    (cd /go/src/golang.org/x/oauth2 && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/text at be25de4 (2017-12-07)
-ENV REV=be25de41fadfae372d6470bda81ca6beb55ef551
-RUN go get -d golang.org/x/text/secure/bidirule `#and 4 other pkgs` &&\
-    (cd /go/src/golang.org/x/text && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo golang.org/x/tools at 5d8e38b (2017-12-10)
-ENV REV=5d8e38b9550d35d893ff8276756b4118ec1bf360
-RUN go get -d golang.org/x/tools/go/ast/astutil `#and 3 other pkgs` &&\
-    (cd /go/src/golang.org/x/tools && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/api at 9a048ca (2017-12-07)
-ENV REV=9a048cac3675aa589c62a35d7d42b25451dd15f1
-RUN go get -d google.golang.org/api/googleapi `#and 6 other pkgs` &&\
-    (cd /go/src/google.golang.org/api && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/genproto at 7f0da29 (2017-11-23)
-ENV REV=7f0da29060c682909f650ad8ed4e515bd74fa12a
-RUN go get -d google.golang.org/genproto/googleapis/api/annotations `#and 4 other pkgs` &&\
-    (cd /go/src/google.golang.org/genproto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Repo google.golang.org/grpc at b8191e5 (2017-12-06)
-ENV REV=b8191e57b23de650278db4d23bf596219e5f3665
-RUN go get -d google.golang.org/grpc `#and 24 other pkgs` &&\
-    (cd /go/src/google.golang.org/grpc && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
-
-# Optimization to speed up iterative development, not necessary for correctness:
-RUN go install cloud.google.com/go/compute/metadata \
-	cloud.google.com/go/datastore \
-	cloud.google.com/go/internal \
-	cloud.google.com/go/internal/atomiccache \
-	cloud.google.com/go/internal/fields \
-	cloud.google.com/go/internal/version \
-	github.com/golang/protobuf/proto \
-	github.com/golang/protobuf/protoc-gen-go/descriptor \
-	github.com/golang/protobuf/ptypes \
-	github.com/golang/protobuf/ptypes/any \
-	github.com/golang/protobuf/ptypes/duration \
-	github.com/golang/protobuf/ptypes/struct \
-	github.com/golang/protobuf/ptypes/timestamp \
-	github.com/golang/protobuf/ptypes/wrappers \
-	github.com/googleapis/gax-go \
-	golang.org/x/net/context \
-	golang.org/x/net/context/ctxhttp \
-	golang.org/x/net/http2 \
-	golang.org/x/net/http2/hpack \
-	golang.org/x/net/idna \
-	golang.org/x/net/internal/timeseries \
-	golang.org/x/net/lex/httplex \
-	golang.org/x/net/trace \
-	golang.org/x/oauth2 \
-	golang.org/x/oauth2/google \
-	golang.org/x/oauth2/internal \
-	golang.org/x/oauth2/jws \
-	golang.org/x/oauth2/jwt \
-	golang.org/x/text/secure/bidirule \
-	golang.org/x/text/transform \
-	golang.org/x/text/unicode/bidi \
-	golang.org/x/text/unicode/norm \
-	golang.org/x/tools/go/ast/astutil \
-	golang.org/x/tools/godoc/static \
-	golang.org/x/tools/imports \
-	google.golang.org/api/googleapi \
-	google.golang.org/api/googleapi/internal/uritemplates \
-	google.golang.org/api/internal \
-	google.golang.org/api/iterator \
-	google.golang.org/api/option \
-	google.golang.org/api/transport/grpc \
-	google.golang.org/genproto/googleapis/api/annotations \
-	google.golang.org/genproto/googleapis/datastore/v1 \
-	google.golang.org/genproto/googleapis/rpc/status \
-	google.golang.org/genproto/googleapis/type/latlng \
-	google.golang.org/grpc \
-	google.golang.org/grpc/balancer \
-	google.golang.org/grpc/balancer/base \
-	google.golang.org/grpc/balancer/roundrobin \
-	google.golang.org/grpc/codes \
-	google.golang.org/grpc/connectivity \
-	google.golang.org/grpc/credentials \
-	google.golang.org/grpc/credentials/oauth \
-	google.golang.org/grpc/encoding \
-	google.golang.org/grpc/grpclb/grpc_lb_v1/messages \
-	google.golang.org/grpc/grpclog \
-	google.golang.org/grpc/internal \
-	google.golang.org/grpc/keepalive \
-	google.golang.org/grpc/metadata \
-	google.golang.org/grpc/naming \
-	google.golang.org/grpc/peer \
-	google.golang.org/grpc/resolver \
-	google.golang.org/grpc/resolver/dns \
-	google.golang.org/grpc/resolver/manual \
-	google.golang.org/grpc/resolver/passthrough \
-	google.golang.org/grpc/stats \
-	google.golang.org/grpc/status \
-	google.golang.org/grpc/tap \
-	google.golang.org/grpc/transport
-# END deps
-
-# Add and compile frontend daemon
-COPY . /go/src/frontend/
-RUN go install frontend
-
-RUN mkdir /app
-
-COPY edit.html /app
-COPY static /app/static
-
-WORKDIR /app
-
-EXPOSE 8080
-ENTRYPOINT ["/go/bin/frontend"]
diff --git a/frontend/Makefile b/frontend/Makefile
deleted file mode 100644
index 2b5f1b5..0000000
--- a/frontend/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-update-deps:
-	go install golang.org/x/build/cmd/gitlock
-	gitlock --update=Dockerfile golang.org/x/playground/frontend
\ No newline at end of file
diff --git a/frontend/compile.go b/frontend/compile.go
deleted file mode 100644
index 0c4deb5..0000000
--- a/frontend/compile.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2011 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 (
-	"io"
-	"net/http"
-)
-
-const runURL = "https://golang.org/compile?output=json"
-
-func (s *server) handleCompile(w http.ResponseWriter, r *http.Request) {
-	if r.Method != "POST" {
-		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
-		return
-	}
-	if err := s.passThru(w, r); err != nil {
-		http.Error(w, "Compile server error.", http.StatusInternalServerError)
-		return
-	}
-}
-
-func (s *server) passThru(w io.Writer, req *http.Request) error {
-	defer req.Body.Close()
-	r, err := http.Post(runURL, req.Header.Get("Content-type"), req.Body)
-	if err != nil {
-		s.log.Errorf("error making POST request: %v", err)
-		return err
-	}
-	defer r.Body.Close()
-	if _, err := io.Copy(w, r.Body); err != nil {
-		s.log.Errorf("error copying response Body: %v", err)
-		return err
-	}
-	return nil
-}
diff --git a/frontend/logger.go b/logger.go
similarity index 100%
rename from frontend/logger.go
rename to logger.go
diff --git a/frontend/main.go b/main.go
similarity index 94%
rename from frontend/main.go
rename to main.go
index 497f49c..acef3f6 100644
--- a/frontend/main.go
+++ b/main.go
@@ -17,6 +17,11 @@
 var log = newStdLogger()
 
 func main() {
+	if len(os.Args) > 1 && os.Args[1] == "test" {
+		test()
+		return
+	}
+
 	s, err := newServer(func(s *server) error {
 		pid := projectID()
 		if pid == "" {
diff --git a/sandbox/play.go b/play.go
similarity index 98%
rename from sandbox/play.go
rename to play.go
index 601666f..fd56676 100644
--- a/sandbox/play.go
+++ b/play.go
@@ -1,4 +1,4 @@
-// Copyright 2014 The Go Authors.  All rights reserved.
+// 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.
 
diff --git a/sandbox/play_test.go b/play_test.go
similarity index 95%
rename from sandbox/play_test.go
rename to play_test.go
index bdf2a0b..69f9dd4 100644
--- a/sandbox/play_test.go
+++ b/play_test.go
@@ -1,4 +1,4 @@
-// Copyright 2015 The Go Authors.  All rights reserved.
+// Copyright 2015 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.
 
diff --git a/sandbox/sandbox.go b/sandbox.go
similarity index 69%
rename from sandbox/sandbox.go
rename to sandbox.go
index 69ca24f..97ba0ba 100644
--- a/sandbox/sandbox.go
+++ b/sandbox.go
@@ -1,23 +1,21 @@
-// Copyright 2014 The Go Authors.  All rights reserved.
+// 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.
 
-// TODO(adg): add logging
-// TODO(proppy): restrict memory use
-// TODO(adg): send exit code to user
+// TODO(andybons): add logging
+// TODO(andybons): restrict memory use
+// TODO(andybons): send exit code to user
 
-// 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 (
+	"context"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"go/parser"
 	"go/token"
 	"io/ioutil"
-	"log"
+	stdlog "log"
 	"net/http"
 	"os"
 	"os/exec"
@@ -28,28 +26,22 @@
 
 const maxRunTime = 2 * time.Second
 
-type Request struct {
+type request struct {
 	Body string
 }
 
-type Response struct {
+type response struct {
 	Errors string
 	Events []Event
 }
 
-func main() {
-	if len(os.Args) > 1 && os.Args[1] == "test" {
-		test()
-		return
-	}
-	http.HandleFunc("/compile", compileHandler)
-	http.HandleFunc("/_ah/health", healthHandler)
-	log.Fatal(http.ListenAndServe(":8080", nil))
-}
-
-func compileHandler(w http.ResponseWriter, r *http.Request) {
-	var req Request
-	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
+func handleCompile(w http.ResponseWriter, r *http.Request) {
+	var req request
+	// Until programs that depend on golang.org/x/tools/godoc/static/playground.js
+	// are updated to always send JSON, this check is in place.
+	if b := r.FormValue("body"); b != "" {
+		req.Body = b
+	} else if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
 		http.Error(w, fmt.Sprintf("error decoding request: %v", err), http.StatusBadRequest)
 		return
 	}
@@ -64,7 +56,8 @@
 	}
 }
 
-func compileAndRun(req *Request) (*Response, error) {
+func compileAndRun(req *request) (*response, error) {
+	// TODO(andybons): Add semaphore to limit number of running programs at once.
 	tmpDir, err := ioutil.TempDir("", "sandbox")
 	if err != nil {
 		return nil, fmt.Errorf("error creating temp directory: %v", err)
@@ -80,7 +73,7 @@
 
 	f, err := parser.ParseFile(fset, in, nil, parser.PackageClauseOnly)
 	if err == nil && f.Name.Name != "main" {
-		return &Response{Errors: "package name must be main"}, nil
+		return &response{Errors: "package name must be main"}, nil
 	}
 
 	exe := filepath.Join(tmpDir, "a.out")
@@ -98,17 +91,19 @@
 			// message before any compile errors; strip it.
 			errs = strings.Replace(errs, "# command-line-arguments\n", "", 1)
 
-			return &Response{Errors: errs}, nil
+			return &response{Errors: errs}, nil
 		}
 		return nil, fmt.Errorf("error building go source: %v", err)
 	}
-	cmd = exec.Command("sel_ldr_x86_64", "-l", "/dev/null", "-S", "-e", exe)
+	ctx, cancel := context.WithTimeout(context.Background(), maxRunTime)
+	defer cancel()
+	cmd = exec.CommandContext(ctx, "sel_ldr_x86_64", "-l", "/dev/null", "-S", "-e", exe)
 	rec := new(Recorder)
 	cmd.Stdout = rec.Stdout()
 	cmd.Stderr = rec.Stderr()
-	if err := runTimeout(cmd, maxRunTime); err != nil {
-		if err == timeoutErr {
-			return &Response{Errors: "process took too long"}, nil
+	if err := cmd.Run(); err != nil {
+		if ctx.Err() == context.DeadlineExceeded {
+			return &response{Errors: "process took too long"}, nil
 		}
 		if _, ok := err.(*exec.ExitError); !ok {
 			return nil, fmt.Errorf("error running sandbox: %v", err)
@@ -118,40 +113,11 @@
 	if err != nil {
 		return nil, fmt.Errorf("error decoding events: %v", err)
 	}
-	return &Response{Events: events}, nil
-}
-
-var timeoutErr = errors.New("process timed out")
-
-func runTimeout(cmd *exec.Cmd, d time.Duration) error {
-	if err := cmd.Start(); err != nil {
-		return err
-	}
-	errc := make(chan error, 1)
-	go func() {
-		errc <- cmd.Wait()
-	}()
-	t := time.NewTimer(d)
-	select {
-	case err := <-errc:
-		t.Stop()
-		return err
-	case <-t.C:
-		cmd.Process.Kill()
-		return timeoutErr
-	}
-}
-
-func healthHandler(w http.ResponseWriter, r *http.Request) {
-	if err := healthCheck(); err != nil {
-		http.Error(w, "Health check failed: "+err.Error(), http.StatusInternalServerError)
-		return
-	}
-	fmt.Fprint(w, "ok")
+	return &response{Events: events}, nil
 }
 
 func healthCheck() error {
-	resp, err := compileAndRun(&Request{Body: healthProg})
+	resp, err := compileAndRun(&request{Body: healthProg})
 	if err != nil {
 		return err
 	}
@@ -174,24 +140,24 @@
 
 func test() {
 	if err := healthCheck(); err != nil {
-		log.Fatal(err)
+		stdlog.Fatal(err)
 	}
 	for _, t := range tests {
-		resp, err := compileAndRun(&Request{Body: t.prog})
+		resp, err := compileAndRun(&request{Body: t.prog})
 		if err != nil {
-			log.Fatal(err)
+			stdlog.Fatal(err)
 		}
 		if t.errors != "" {
 			if resp.Errors != t.errors {
-				log.Fatalf("resp.Errors = %q, want %q", resp.Errors, t.errors)
+				stdlog.Fatalf("resp.Errors = %q, want %q", resp.Errors, t.errors)
 			}
 			continue
 		}
 		if resp.Errors != "" {
-			log.Fatal(resp.Errors)
+			stdlog.Fatal(resp.Errors)
 		}
 		if len(resp.Events) != 1 || !strings.Contains(resp.Events[0].Message, t.want) {
-			log.Fatalf("unexpected output: %v, want %q", resp.Events, t.want)
+			stdlog.Fatalf("unexpected output: %v, want %q", resp.Events, t.want)
 		}
 	}
 	fmt.Println("OK")
diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile
deleted file mode 100644
index b20cbc4..0000000
--- a/sandbox/Dockerfile
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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 debian:jessie
-
-ENV GOPATH /go
-ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
-ENV GOROOT_BOOTSTRAP /usr/local/gobootstrap
-ENV GO_VERSION 1.9.2
-
-# Fake time
-COPY enable-fake-time.patch /usr/local/sandbox/
-# Fake file system
-COPY fake_fs.lst /usr/local/sandbox/
-
-RUN set -x && buildDeps='curl ca-certificates bzip2'; \
-    apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* && \
-    curl -s https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/44.0.2403.157/naclsdk_linux.tar.bz2 | tar -xj -C /usr/local/bin --strip-components=2 pepper_44/tools/sel_ldr_x86_64 && \
-    apt-get purge -y --auto-remove $buildDeps
-
-RUN set -x && buildDeps='curl ca-certificates gcc patch libc6-dev'; \
-    apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* && \
-    # Get the Go binary.
-    curl -sSL https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz -o /tmp/go.tar.gz && \
-    curl -sSL https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz.sha256 -o /tmp/go.tar.gz.sha256 && \
-    echo "$(cat /tmp/go.tar.gz.sha256)  /tmp/go.tar.gz" | sha256sum -c - && \
-    tar -C /usr/local/ -vxzf /tmp/go.tar.gz && \
-    rm /tmp/go.tar.gz /tmp/go.tar.gz.sha256 && \
-    # Make a copy for GOROOT_BOOTSTRAP, because we rebuild the toolchain and make.bash removes bin/go as its first step.
-    cp -R /usr/local/go $GOROOT_BOOTSTRAP && \
-    # Apply the fake time and fake filesystem patches.
-    patch /usr/local/go/src/runtime/rt0_nacl_amd64p32.s /usr/local/sandbox/enable-fake-time.patch && \
-    cd /usr/local/go && go run misc/nacl/mkzip.go -p syscall /usr/local/sandbox/fake_fs.lst src/syscall/fstest_nacl.go && \
-    # Re-build the Go toolchain.
-    cd /usr/local/go/src && GOOS=nacl GOARCH=amd64p32 ./make.bash --no-clean && \
-    # Clean up.
-    rm -rf $GOROOT_BOOTSTRAP && \
-    apt-get purge -y --auto-remove $buildDeps
-
-# Add and compile tour packages
-RUN set -x && buildDeps='git ca-certificates'; \
-    apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* && \
-    GOOS=nacl GOARCH=amd64p32 go get \
-    golang.org/x/tour/pic \
-    golang.org/x/tour/reader \
-    golang.org/x/tour/tree \
-    golang.org/x/tour/wc \
-    golang.org/x/talks/2016/applicative/google && \
-    rm -rf $GOPATH/src/golang.org/x/tour/.git && \
-    rm -rf $GOPATH/src/golang.org/x/talks/.git && \
-    apt-get purge -y --auto-remove $buildDeps
-
-# Add tour packages under their old import paths (so old snippets still work)
-RUN mkdir -p $GOPATH/src/code.google.com/p/go-tour && \
-    cp -R $GOPATH/src/golang.org/x/tour/* $GOPATH/src/code.google.com/p/go-tour/ && \
-    sed -i 's_// import_// public import_' $(find $GOPATH/src/code.google.com/p/go-tour/ -name *.go) && \
-    go install \
-    code.google.com/p/go-tour/pic \
-    code.google.com/p/go-tour/reader \
-    code.google.com/p/go-tour/tree \
-    code.google.com/p/go-tour/wc
-
-# Add and compile sandbox daemon
-COPY . /go/src/sandbox/
-RUN go install sandbox
-
-# Run tests
-RUN /go/bin/sandbox test
-
-EXPOSE 8080
-ENTRYPOINT ["/go/bin/sandbox"]
diff --git a/sandbox/Makefile b/sandbox/Makefile
deleted file mode 100644
index 72cdfc2..0000000
--- a/sandbox/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-docker: Dockerfile
-	docker build -t playground/sandbox .
-
-test: docker
-	go test
-	docker run --rm playground/sandbox test
diff --git a/sandbox/app-flex.yaml b/sandbox/app-flex.yaml
deleted file mode 100644
index fad29f8..0000000
--- a/sandbox/app-flex.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-service: sandbox-flex
-runtime: custom
-env: flex
-
-automatic_scaling:
-  min_num_instances: 5
-  max_num_instances: 20
-
-health_check:
-  check_interval_sec: 20
-  restart_threshold: 10
diff --git a/sandbox/app.yaml b/sandbox/app.yaml
deleted file mode 100644
index 8e6a30d..0000000
--- a/sandbox/app.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-service: sandbox
-runtime: custom
-vm: true
-
-automatic_scaling:
-  min_num_instances: 5
-  max_num_instances: 20
-
-health_check:
-  check_interval_sec: 20
-  restart_threshold: 10
-
-handlers:
-- url: /.*
-  script: _go_app
-
diff --git a/sandbox/container-vm.yaml b/sandbox/container-vm.yaml
deleted file mode 100644
index 3bd498f..0000000
--- a/sandbox/container-vm.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-version: v1beta2
-containers:
-  - name: sandbox
-    # TODO(proppy): publish the sandbox image to the hub
-    image: golang/playground-sandbox
-    ports:
-      - containerPort: 8080
-        hostPort: 80
diff --git a/frontend/server.go b/server.go
similarity index 91%
rename from frontend/server.go
rename to server.go
index 136124c..350b384 100644
--- a/frontend/server.go
+++ b/server.go
@@ -48,9 +48,9 @@
 
 func (s *server) init() {
 	s.mux.HandleFunc("/", s.handleEdit)
-	s.mux.HandleFunc("/compile", s.handleCompile)
 	s.mux.HandleFunc("/fmt", handleFmt)
 	s.mux.HandleFunc("/share", s.handleShare)
+	s.mux.HandleFunc("/compile", handleCompile)
 	s.mux.HandleFunc("/playground.js", s.handlePlaygroundJS)
 	s.mux.HandleFunc("/favicon.ico", handleFavicon)
 	s.mux.HandleFunc("/_ah/health", handleHealthCheck)
@@ -70,7 +70,11 @@
 }
 
 func handleHealthCheck(w http.ResponseWriter, r *http.Request) {
-	fmt.Fprintf(w, "ok")
+	if err := healthCheck(); err != nil {
+		http.Error(w, "Health check failed: "+err.Error(), http.StatusInternalServerError)
+		return
+	}
+	fmt.Fprint(w, "ok")
 }
 
 func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
diff --git a/frontend/server_test.go b/server_test.go
similarity index 100%
rename from frontend/server_test.go
rename to server_test.go
diff --git a/frontend/share.go b/share.go
similarity index 100%
rename from frontend/share.go
rename to share.go
diff --git a/frontend/static/favicon.ico b/static/favicon.ico
similarity index 100%
rename from frontend/static/favicon.ico
rename to static/favicon.ico
Binary files differ
diff --git a/frontend/static/godoc.css b/static/godoc.css
similarity index 100%
rename from frontend/static/godoc.css
rename to static/godoc.css
diff --git a/frontend/static/gopher.png b/static/gopher.png
similarity index 100%
rename from frontend/static/gopher.png
rename to static/gopher.png
Binary files differ
diff --git a/frontend/static/jquery-linedtextarea.js b/static/jquery-linedtextarea.js
similarity index 100%
rename from frontend/static/jquery-linedtextarea.js
rename to static/jquery-linedtextarea.js
diff --git a/frontend/static/playground-embed.js b/static/playground-embed.js
similarity index 100%
rename from frontend/static/playground-embed.js
rename to static/playground-embed.js
diff --git a/frontend/static/style.css b/static/style.css
similarity index 100%
rename from frontend/static/style.css
rename to static/style.css
diff --git a/frontend/store.go b/store.go
similarity index 100%
rename from frontend/store.go
rename to store.go