playground: drop NaCL support

It is longer being used in production as of Go 1.14beta1.

Updates golang/go#25224

Change-Id: I5a83c88387f722bb0be476d0500f74a061827770
Reviewed-on: https://go-review.googlesource.com/c/playground/+/227351
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
diff --git a/Dockerfile b/Dockerfile
index c3a5c01..b402acc 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,14 +4,6 @@
 
 ARG GO_VERSION=go1.14.1
 
-############################################################################
-FROM debian:buster AS nacl
-
-RUN apt-get update && apt-get install -y --no-install-recommends curl bzip2 ca-certificates
-
-RUN curl -s https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/trunk.544461/naclsdk_linux.tar.bz2 | tar -xj -C /tmp --strip-components=2 pepper_67/tools/sel_ldr_x86_64
-
-############################################################################
 FROM debian:buster AS build
 LABEL maintainer="golang-dev@googlegroups.com"
 
@@ -20,87 +12,54 @@
 
 ENV GOPATH /go
 ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
-ENV GOROOT_BOOTSTRAP /usr/local/gobootstrap
-ENV GO_BOOTSTRAP_VERSION go1.13.9
+ENV GO_BOOTSTRAP_VERSION go1.14.1
 ARG GO_VERSION
 ENV GO_VERSION ${GO_VERSION}
 
-# Fake time
-COPY enable-fake-time.patch /usr/local/playground/
-# Fake file system
-COPY fake_fs.lst /usr/local/playground/
-
-# Get a bootstrap version of Go for building from source.
+# Get a version of Go for building the playground
 RUN curl -sSL https://dl.google.com/go/$GO_BOOTSTRAP_VERSION.linux-amd64.tar.gz -o /tmp/go.tar.gz
 RUN curl -sSL https://dl.google.com/go/$GO_BOOTSTRAP_VERSION.linux-amd64.tar.gz.sha256 -o /tmp/go.tar.gz.sha256
 RUN echo "$(cat /tmp/go.tar.gz.sha256) /tmp/go.tar.gz" | sha256sum -c -
-RUN mkdir -p $GOROOT_BOOTSTRAP
-RUN tar --strip=1 -C $GOROOT_BOOTSTRAP -vxzf /tmp/go.tar.gz
-
-# Fetch Go source for tag $GO_VERSION.
-RUN git clone --depth=1 --branch=$GO_BOOTSTRAP_VERSION https://go.googlesource.com/go /usr/local/go
-# Apply the fake time and fake filesystem patches.
-RUN patch /usr/local/go/src/runtime/rt0_nacl_amd64p32.s /usr/local/playground/enable-fake-time.patch
-RUN cd /usr/local/go && $GOROOT_BOOTSTRAP/bin/go run misc/nacl/mkzip.go -p syscall /usr/local/playground/fake_fs.lst src/syscall/fstest_nacl.go
-# Build the Go toolchain.
-RUN cd /usr/local/go/src && GOOS=nacl GOARCH=amd64p32 ./make.bash --no-clean
+RUN mkdir -p /usr/local/go
+RUN tar --strip=1 -C /usr/local/go -vxzf /tmp/go.tar.gz
 
 RUN mkdir /gocache
 ENV GOCACHE /gocache
 ENV GO111MODULE on
 ENV GOPROXY=https://proxy.golang.org
 
+# Compile Go at target sandbox version and install standard library with --tags=faketime.
+WORKDIR /usr/local
+RUN git clone https://go.googlesource.com/go go-faketime && cd go-faketime && git reset --hard $GO_VERSION
+WORKDIR /usr/local/go-faketime/src
+RUN ./make.bash
+ENV GOROOT /usr/local/go-faketime
+RUN ../bin/go install --tags=faketime std
+
 COPY go.mod /go/src/playground/go.mod
 COPY go.sum /go/src/playground/go.sum
 WORKDIR /go/src/playground
-
-# Pre-build some packages to speed final install later.
-RUN go install cloud.google.com/go/compute/metadata
-RUN go install cloud.google.com/go/datastore
-RUN go install github.com/bradfitz/gomemcache/memcache
-RUN go install golang.org/x/tools/godoc/static
-RUN go install golang.org/x/tools/imports
-RUN go install github.com/rogpeppe/go-internal/modfile
-RUN go install github.com/rogpeppe/go-internal/txtar
+RUN go mod download
 
 # Add and compile playground daemon
 COPY . /go/src/playground/
-WORKDIR /go/src/playground
 RUN go install
 
 ############################################################################
-# Temporary Docker stage to add a pre-Go1.14 $GOROOT into our
-# container for early linux/amd64 testing.
-FROM golang:1.13 AS temp_pre_go14
-
-ENV BUILD_DEPS 'curl git gcc patch libc6-dev ca-certificates'
-RUN apt-get update && apt-get install -y --no-install-recommends ${BUILD_DEPS}
-
-ARG GO_VERSION
-ENV GO_VERSION ${GO_VERSION}
-RUN cd /usr/local && git clone https://go.googlesource.com/go go1.14 && cd go1.14 && git reset --hard $GO_VERSION
-WORKDIR /usr/local/go1.14/src
-RUN ./make.bash
-ENV GOROOT /usr/local/go1.14
-RUN ../bin/go install --tags=faketime std
-
-############################################################################
 # Final stage.
 FROM debian:buster
 
 RUN apt-get update && apt-get install -y git ca-certificates --no-install-recommends
 
-COPY --from=build /usr/local/go /usr/local/go
-COPY --from=nacl /tmp/sel_ldr_x86_64 /usr/local/bin
-COPY --from=temp_pre_go14 /usr/local/go1.14 /usr/local/go1.14
+COPY --from=build /usr/local/go-faketime /usr/local/go-faketime
 
 ARG GO_VERSION
 ENV GO_VERSION ${GO_VERSION}
 ENV GOPATH /go
-ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
+ENV PATH /usr/local/go-faketime/bin:$GOPATH/bin:$PATH
 
 # Add and compile tour packages
-RUN GOOS=nacl GOARCH=amd64p32 go get \
+RUN go get \
     golang.org/x/tour/pic \
     golang.org/x/tour/reader \
     golang.org/x/tour/tree \
diff --git a/Makefile b/Makefile
index 56ff930..e96fbd8 100644
--- a/Makefile
+++ b/Makefile
@@ -12,24 +12,20 @@
 runlocal:
 	docker network create sandnet || true
 	docker kill play_dev || true
-	docker run --name=play_dev --rm --network=sandnet --env SANDBOX_BACKEND_URL="http://sandbox_dev.sandnet/run" -ti -p 127.0.0.1:8081:8080/tcp golang/playground
+	docker run --name=play_dev --rm --network=sandnet -ti -p 127.0.0.1:8081:8080/tcp golang/playground --backend-url="http://sandbox_dev.sandnet/run"
 
 test_go:
 	# Run fast tests first: (and tests whether, say, things compile)
 	GO111MODULE=on go test -v
 
-test_nacl: docker
-	docker kill sandbox_front_test || true
-	docker run --rm --name=sandbox_front_test --network=sandnet -t golang/playground testnacl
-
 test_gvisor: docker
 	docker kill sandbox_front_test || true
-	docker run --rm --name=sandbox_front_test --network=sandnet -t golang/playground test
+	docker run --rm --name=sandbox_front_test --network=sandnet -t golang/playground --runtests
 
 # Note: test_gvisor is not included in "test" yet, because it requires
 # running a separate server first ("make runlocal" in the sandbox
 # directory)
-test: test_go test_nacl
+test: test_go
 
 update-cloudbuild-trigger:
 	# The gcloud CLI doesn't yet support updating a trigger.
diff --git a/main.go b/main.go
index 6b1105e..733a6a0 100644
--- a/main.go
+++ b/main.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"flag"
 	"fmt"
 	"net/http"
 	"os"
@@ -16,7 +17,13 @@
 
 var log = newStdLogger()
 
+var (
+	runtests   = flag.Bool("runtests", false, "Run integration tests instead of Playground server.")
+	backendURL = flag.String("backend-url", "", "URL for sandbox backend that runs Go binaries.")
+)
+
 func main() {
+	flag.Parse()
 	s, err := newServer(func(s *server) error {
 		pid := projectID()
 		if pid == "" {
@@ -41,13 +48,13 @@
 		log.Fatalf("Error creating server: %v", err)
 	}
 
-	if len(os.Args) > 1 && os.Args[1] == "test" {
+	if *runtests {
 		s.test()
 		return
 	}
-	if len(os.Args) > 1 && os.Args[1] == "testnacl" {
-		s.testNacl()
-		return
+	if *backendURL != "" {
+		// TODO(golang.org/issue/25224) - Remove environment variable and use a flag.
+		os.Setenv("SANDBOX_BACKEND_URL", *backendURL)
 	}
 
 	port := os.Getenv("PORT")
diff --git a/sandbox.go b/sandbox.go
index 5fbc89c..effe8a1 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -373,42 +373,22 @@
 		}
 	}
 
-	// TODO: simplify this once Go 1.14 is out. We should remove
-	// the //play:gvisor substring hack and DEBUG_FORCE_GVISOR and
-	// instead implement https://golang.org/issue/33629 to
-	// officially support different Go versions (Go tip + past two
-	// releases).
-	useGvisor := os.Getenv("GO_VERSION") >= "go1.14" ||
-		os.Getenv("DEBUG_FORCE_GVISOR") == "1" ||
-		strings.Contains(req.Body, "//play:gvisor\n")
-
 	exe := filepath.Join(tmpDir, "a.out")
 	goCache := filepath.Join(tmpDir, "gocache")
 
 	buildCtx, cancel := context.WithTimeout(ctx, maxCompileTime)
 	defer cancel()
-	goBin := "go"
-	if useGvisor {
-		goBin = "/usr/local/go1.14/bin/go"
-	}
-	cmd := exec.CommandContext(buildCtx, goBin,
-		"build",
-		"-o", exe,
-		"-tags=faketime", // required for Go 1.14+, no-op before
-		buildPkgArg)
+	cmd := exec.CommandContext(ctx, "/usr/local/go-faketime/bin/go", "build", "-o", exe, "-tags=faketime", buildPkgArg)
 	cmd.Dir = tmpDir
 	var goPath string
-	if useGvisor {
-		cmd.Env = []string{"GOOS=linux", "GOARCH=amd64", "GOROOT=/usr/local/go1.14"}
-	} else {
-		cmd.Env = []string{"GOOS=nacl", "GOARCH=amd64p32"}
-	}
+	cmd.Env = []string{"GOOS=linux", "GOARCH=amd64", "GOROOT=/usr/local/go-faketime"}
 	cmd.Env = append(cmd.Env, "GOCACHE="+goCache)
 	if useModules {
 		// Create a GOPATH just for modules to be downloaded
 		// into GOPATH/pkg/mod.
 		goPath, err = ioutil.TempDir("", "gopath")
 		if err != nil {
+			log.Printf("error creating temp directory: %v", err)
 			return nil, fmt.Errorf("error creating temp directory: %v", err)
 		}
 		defer os.RemoveAll(goPath)
@@ -438,72 +418,53 @@
 		}
 		return nil, fmt.Errorf("error building go source: %v", err)
 	}
-	runCtx, cancel := context.WithTimeout(ctx, maxRunTime)
-	defer cancel()
 	rec := new(Recorder)
 	var exitCode int
-	if useGvisor {
-		const maxBinarySize = 100 << 20 // copied from sandbox backend; TODO: unify?
-		if fi, err := os.Stat(exe); err != nil || fi.Size() == 0 || fi.Size() > maxBinarySize {
-			if err != nil {
-				return nil, fmt.Errorf("failed to stat binary: %v", err)
-			}
-			return nil, fmt.Errorf("invalid binary size %d", fi.Size())
-		}
-		exeBytes, err := ioutil.ReadFile(exe)
+	const maxBinarySize = 100 << 20 // copied from sandbox backend; TODO: unify?
+	if fi, err := os.Stat(exe); err != nil || fi.Size() == 0 || fi.Size() > maxBinarySize {
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("failed to stat binary: %v", err)
 		}
-		req, err := http.NewRequestWithContext(runCtx, "POST", sandboxBackendURL(), bytes.NewReader(exeBytes))
-		if err != nil {
-			return nil, err
-		}
-		req.Header.Add("Idempotency-Key", "1") // lets Transport do retries with a POST
-		if testParam != "" {
-			req.Header.Add("X-Argument", testParam)
-		}
-		req.GetBody = func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(exeBytes)), nil }
-		res, err := sandboxBackendClient().Do(req)
-		if err != nil {
-			return nil, err
-		}
-		defer res.Body.Close()
-		if res.StatusCode != http.StatusOK {
-			return nil, fmt.Errorf("unexpected response from backend: %v", res.Status)
-		}
-		var execRes sandboxtypes.Response
-		if err := json.NewDecoder(res.Body).Decode(&execRes); err != nil {
-			log.Printf("JSON decode error from backend: %v", err)
-			return nil, errors.New("error parsing JSON from backend")
-		}
-		if execRes.Error != "" {
-			return &response{Errors: execRes.Error}, nil
-		}
-		exitCode = execRes.ExitCode
-		rec.Stdout().Write(execRes.Stdout)
-		rec.Stderr().Write(execRes.Stderr)
-	} else {
-		cmd := exec.CommandContext(runCtx, "sel_ldr_x86_64", "-l", "/dev/null", "-S", "-e", exe, testParam)
-		cmd.Stdout = rec.Stdout()
-		cmd.Stderr = rec.Stderr()
-		if err := cmd.Run(); err != nil {
-			if runCtx.Err() == context.DeadlineExceeded {
-				// Send what was captured before the timeout.
-				events, err := rec.Events()
-				if err != nil {
-					return nil, fmt.Errorf("error decoding events: %v", err)
-				}
-				return &response{Errors: "process took too long", Events: events}, nil
-			}
-			exitErr, ok := err.(*exec.ExitError)
-			if !ok {
-				return nil, fmt.Errorf("error running sandbox: %v", err)
-			}
-			exitCode = exitErr.ExitCode()
-		}
+		return nil, fmt.Errorf("invalid binary size %d", fi.Size())
 	}
+	exeBytes, err := ioutil.ReadFile(exe)
+	if err != nil {
+		return nil, err
+	}
+	runCtx, cancel := context.WithTimeout(ctx, maxRunTime)
+	defer cancel()
+	sreq, err := http.NewRequestWithContext(runCtx, "POST", sandboxBackendURL(), bytes.NewReader(exeBytes))
+	if err != nil {
+		return nil, fmt.Errorf("NewRequestWithContext %q: %w", sandboxBackendURL(), err)
+	}
+	sreq.Header.Add("Idempotency-Key", "1") // lets Transport do retries with a POST
+	if testParam != "" {
+		sreq.Header.Add("X-Argument", testParam)
+	}
+	sreq.GetBody = func() (io.ReadCloser, error) { return ioutil.NopCloser(bytes.NewReader(exeBytes)), nil }
+	res, err := sandboxBackendClient().Do(sreq)
+	if err != nil {
+		return nil, fmt.Errorf("POST %q: %w", sandboxBackendURL(), err)
+	}
+	defer res.Body.Close()
+	if res.StatusCode != http.StatusOK {
+		log.Printf("unexpected response from backend: %v", res.Status)
+		return nil, fmt.Errorf("unexpected response from backend: %v", res.Status)
+	}
+	var execRes sandboxtypes.Response
+	if err := json.NewDecoder(res.Body).Decode(&execRes); err != nil {
+		log.Printf("JSON decode error from backend: %v", err)
+		return nil, errors.New("error parsing JSON from backend")
+	}
+	if execRes.Error != "" {
+		return &response{Errors: execRes.Error}, nil
+	}
+	exitCode = execRes.ExitCode
+	rec.Stdout().Write(execRes.Stdout)
+	rec.Stderr().Write(execRes.Stderr)
 	events, err := rec.Events()
 	if err != nil {
+		log.Printf("error decoding events: %v", err)
 		return nil, fmt.Errorf("error decoding events: %v", err)
 	}
 	var fails int
diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile
index ceb5401..f2da1d5 100644
--- a/sandbox/Dockerfile
+++ b/sandbox/Dockerfile
@@ -4,7 +4,7 @@
 # environment so the play-sandbox server can connect to the host's
 # docker daemon, which has the gvisor "runsc" runtime available.
 
-FROM golang:1.13 AS build
+FROM golang:1.14 AS build
 
 COPY . /go/src/playground
 WORKDIR /go/src/playground/sandbox
@@ -15,7 +15,7 @@
 RUN apt-get update
 
 # Extra stuff for occasional debugging:
-RUN apt-get install --yes strace lsof emacs25-nox net-tools tcpdump procps
+RUN apt-get install --yes strace lsof emacs-nox net-tools tcpdump procps
 
 # Install Docker CLI:
 RUN apt-get install --yes \
diff --git a/sandbox/Dockerfile.gvisor b/sandbox/Dockerfile.gvisor
index 4218887..464aaec 100644
--- a/sandbox/Dockerfile.gvisor
+++ b/sandbox/Dockerfile.gvisor
@@ -9,25 +9,10 @@
 FROM golang/playground-sandbox AS server
 
 ############################################################################
-# Temporary nacl compatibility for development & incremental
-# deployment purposes, so we can run the new server architecture in
-# nacl mode for a bit, then opt-in linux/amd64 gvisor mode, and
-# then once Go 1.14 is out for real we remove the nacl option and
-# delete all the nacl code.
-FROM debian:buster AS nacl
-RUN apt-get update && apt-get install -y --no-install-recommends curl bzip2 ca-certificates
-RUN curl -s https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/trunk.544461/naclsdk_linux.tar.bz2 | tar -xj -C /tmp --strip-components=2 pepper_67/tools/sel_ldr_x86_64
-
-
-############################################################################
 # This is the actual environment things run in: a minimal busybox with glibc
 # binaries so we can use cgo.
 FROM busybox:glibc
 
 COPY --from=server /usr/local/bin/play-sandbox /usr/local/bin/play-sandbox
 
-# And this, temporarily, for being able to test the old nacl binaries
-# with the new sandbox:
-COPY --from=nacl /tmp/sel_ldr_x86_64 /usr/local/bin
-
 ENTRYPOINT ["/usr/local/bin/play-sandbox"]
diff --git a/sandbox/sandbox.go b/sandbox/sandbox.go
index 9b8bcba..603088b 100644
--- a/sandbox/sandbox.go
+++ b/sandbox/sandbox.go
@@ -255,27 +255,12 @@
 		log.Fatalf("writing header to stderr: %v", err)
 	}
 
-	// As part of a temporary transition plan, we also support
-	// running nacl binaries in this sandbox. The point isn't to
-	// double sandbox things as much as it is to let us transition
-	// things in steps: first to split the sandbox into two parts
-	// (frontend & backend), and then to change the type of binary
-	// (nacl to linux/amd64). This means we can do step 1 of the
-	// migration during the Go 1.13 dev cycle and have less
-	// risk/rush during the Go 1.14 release, which should just be
-	// a flag flip.
-	// This isn't a perfect heuristic, but it works and it's cheap:
-	isNacl := bytes.Contains(slurp, []byte("_rt0_amd64p32_nacl"))
-
 	cmd := exec.Command(binPath)
-	if isNacl {
-		cmd = exec.Command("/usr/local/bin/sel_ldr_x86_64", "-l", "/dev/null", "-S", "-e", binPath)
-	}
 	cmd.Args = append(cmd.Args, meta.Args...)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	if err := cmd.Start(); err != nil {
-		log.Fatal(err)
+		log.Fatalf("cmd.Start(): %v", err)
 	}
 	err = cmd.Wait()
 	os.Remove(binPath) // not that it matters much, this container will be nuked
diff --git a/tests.go b/tests.go
index 454b02a..9e0d55f 100644
--- a/tests.go
+++ b/tests.go
@@ -27,12 +27,6 @@
 	wantVetErrors      string
 }
 
-func (s *server) testNacl() {
-	log.Printf("testing nacl mode")
-	os.Setenv("GO_VERSION", "")
-	s.runTests()
-}
-
 func (s *server) test() {
 	if _, err := net.ResolveIPAddr("ip", "sandbox_dev.sandnet."); err != nil {
 		log.Fatalf("sandbox_dev.sandnet not available")