all: support Go 1.16 in the Go Playground

This makes a number of changes in order to support modules being on by
default in Go 1.16.

- Change default Go version in Dockerfile to go1.16
- Change default Go version in sandbox/Dockerfile to go1.16
- Build playground web server with GO_VERSION instead of
GO_BOOTSTRAP_VERSION (for golang/go#40319)
- Drop code related to non-module codepaths. This should keep us
future-proof for later versions of Go.

This works for me testing locally with gVisor's runsc.

Fixes golang/go#44389
Fixes golang/go#40319

Change-Id: Ib99510e662658b75c401567314c3e8ed612002a0
Reviewed-on: https://go-review.googlesource.com/c/playground/+/295649
Trust: Alexander Rakoczy <alex@golang.org>
Run-TryBot: Alexander Rakoczy <alex@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/Dockerfile b/Dockerfile
index 5a314c1..f779131 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,48 +2,74 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-ARG GO_VERSION=go1.14.1
+# The playground builds Go from a bootstrap version for two reasons:
+# - The playground deployment is triggered before the artifacts are
+#   published for the latest version of Go.
+# - The sandbox builds the Go standard library with a custom build
+#   flag called faketime.
 
-FROM debian:buster AS go-faketime
+# GO_VERSION is provided by Cloud Build, and is set to the latest
+# version of Go. See the configuration in the deploy directory.
+ARG GO_VERSION=go1.16
+
+############################################################################
+# Build Go at GO_VERSION, and build faketime standard library.
+FROM debian:buster AS build-go
 LABEL maintainer="golang-dev@googlegroups.com"
 
 ENV BUILD_DEPS 'curl git gcc patch libc6-dev ca-certificates'
 RUN apt-get update && apt-get install -y ${BUILD_DEPS} --no-install-recommends
 
 ENV GOPATH /go
-ENV PATH /usr/local/go/bin:$GOPATH/bin:$PATH
-ENV GO_BOOTSTRAP_VERSION go1.14.1
+ENV GOROOT_BOOTSTRAP=/usr/local/go-bootstrap
+ENV GO_BOOTSTRAP_VERSION go1.16
 ARG GO_VERSION
 ENV GO_VERSION ${GO_VERSION}
 
-# Get a version of Go for building the playground
+# Get a bootstrap version of Go for building GO_VERSION. At the time
+# of this Dockerfile being built, GO_VERSION's artifacts may not yet
+# be published.
 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 /usr/local/go
-RUN tar --strip=1 -C /usr/local/go -vxzf /tmp/go.tar.gz
+RUN mkdir -p $GOROOT_BOOTSTRAP
+RUN tar --strip=1 -C $GOROOT_BOOTSTRAP -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.
+# Compile Go at target version in /usr/local/go.
 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 git clone https://go.googlesource.com/go go && cd go && git reset --hard $GO_VERSION
+WORKDIR /usr/local/go/src
 RUN ./make.bash
+
+# Make a copy in /usr/local/go-faketime where the standard library
+# is installed with -tags=faketime.
+RUN cp -R /usr/local/go /usr/local/go-faketime
 ENV GOROOT /usr/local/go-faketime
+WORKDIR /usr/local/go-faketime/src
 RUN ../bin/go install --tags=faketime std
 
-FROM golang:1.14 as build-playground
+############################################################################
+# Build playground web server.
+FROM debian:buster as build-playground
 
+RUN apt-get update && apt-get install -y ca-certificates --no-install-recommends
+# Build playground from Go built at GO_VERSION.
+COPY --from=build-go /usr/local/go /usr/local/go
+ENV GOROOT /usr/local/go
+ENV GOPATH /go
+ENV PATH="/go/bin:/usr/local/go/bin:${PATH}"
+# Cache dependencies for efficient Dockerfile building.
 COPY go.mod /go/src/playground/go.mod
 COPY go.sum /go/src/playground/go.sum
 WORKDIR /go/src/playground
 RUN go mod download
 
-# Add and compile playground daemon
+# Add and compile playground daemon.
 COPY . /go/src/playground/
 RUN go install
 
@@ -53,43 +79,19 @@
 
 RUN apt-get update && apt-get install -y git ca-certificates --no-install-recommends
 
-COPY --from=go-faketime /usr/local/go-faketime /usr/local/go-faketime
+COPY --from=build-go /usr/local/go-faketime /usr/local/go-faketime
 
 ARG GO_VERSION
 ENV GO_VERSION ${GO_VERSION}
 ENV GOPATH /go
-ENV PATH /usr/local/go-faketime/bin:$GOPATH/bin:$PATH
-
-# Add and compile tour packages
-RUN 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/content/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
+ENV PATH="/go/bin:/usr/local/go-faketime/bin:${PATH}"
 
 RUN mkdir /app
-
 COPY --from=build-playground /go/bin/playground /app
 COPY edit.html /app
 COPY static /app/static
 COPY examples /app/examples
 WORKDIR /app
 
-# Whether we allow third-party imports via proxy.golang.org:
-ENV ALLOW_PLAY_MODULE_DOWNLOADS true
-
 EXPOSE 8080
 ENTRYPOINT ["/app/playground"]
diff --git a/sandbox.go b/sandbox.go
index 8931100..9a8e8a3 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -396,8 +396,6 @@
 	goPath string
 	// exePath is the path to the built binary.
 	exePath string
-	// useModules is true if the binary was built with module support.
-	useModules bool
 	// testParam is set if tests should be run when running the binary.
 	testParam string
 	// errorMessage is an error message string to be returned to the user.
@@ -408,7 +406,7 @@
 
 // cleanup cleans up the temporary goPath created when building with module support.
 func (b *buildResult) cleanup() error {
-	if b.useModules && b.goPath != "" {
+	if b.goPath != "" {
 		return os.RemoveAll(b.goPath)
 	}
 	return nil
@@ -435,8 +433,7 @@
 		}
 	}
 
-	br.useModules = allowModuleDownloads(files)
-	if !files.Contains("go.mod") && br.useModules {
+	if !files.Contains("go.mod") {
 		files.AddFile("go.mod", []byte("module play\n"))
 	}
 
@@ -471,20 +468,16 @@
 	cmd.Dir = tmpDir
 	cmd.Env = []string{"GOOS=linux", "GOARCH=amd64", "GOROOT=/usr/local/go-faketime"}
 	cmd.Env = append(cmd.Env, "GOCACHE="+goCache)
-	if br.useModules {
-		// Create a GOPATH just for modules to be downloaded
-		// into GOPATH/pkg/mod.
-		cmd.Args = append(cmd.Args, "-modcacherw")
-		br.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)
-		}
-		cmd.Env = append(cmd.Env, "GO111MODULE=on", "GOPROXY="+playgroundGoproxy())
-	} else {
-		br.goPath = os.Getenv("GOPATH")              // contains old code.google.com/p/go-tour, etc
-		cmd.Env = append(cmd.Env, "GO111MODULE=off") // in case it becomes on by default later
+	// Create a GOPATH just for modules to be downloaded
+	// into GOPATH/pkg/mod.
+	cmd.Args = append(cmd.Args, "-modcacherw")
+	cmd.Args = append(cmd.Args, "-mod=mod")
+	br.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)
 	}
+	cmd.Env = append(cmd.Env, "GO111MODULE=on", "GOPROXY="+playgroundGoproxy())
 	cmd.Args = append(cmd.Args, buildPkgArg)
 	cmd.Env = append(cmd.Env, "GOPATH="+br.goPath)
 	out := &bytes.Buffer{}
@@ -521,7 +514,7 @@
 	}
 	if vet {
 		// TODO: do this concurrently with the execution to reduce latency.
-		br.vetOut, err = vetCheckInDir(tmpDir, br.goPath, br.useModules)
+		br.vetOut, err = vetCheckInDir(tmpDir, br.goPath)
 		if err != nil {
 			return nil, fmt.Errorf("running vet: %v", err)
 		}
@@ -567,21 +560,6 @@
 	return execRes, nil
 }
 
-// allowModuleDownloads reports whether the code snippet in src should be allowed
-// to download modules.
-func allowModuleDownloads(files *fileSet) bool {
-	if files.Num() == 1 && bytes.Contains(files.Data(progName), []byte(`"code.google.com/p/go-tour/`)) {
-		// This domain doesn't exist anymore but we want old snippets using
-		// these packages to still run, so the Dockerfile adds these packages
-		// at this name in $GOPATH. Any snippets using this old name wouldn't
-		// have expected (or been able to use) third-party packages anyway,
-		// so disabling modules and proxy fetches is acceptable.
-		return false
-	}
-	v, _ := strconv.ParseBool(os.Getenv("ALLOW_PLAY_MODULE_DOWNLOADS"))
-	return v
-}
-
 // playgroundGoproxy returns the GOPROXY environment config the playground should use.
 // It is fetched from the environment variable PLAY_GOPROXY. A missing or empty
 // value for PLAY_GOPROXY returns the default value of https://proxy.golang.org.
diff --git a/sandbox/Dockerfile b/sandbox/Dockerfile
index bd32b1e..5a19a1b 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.14 AS build
+FROM golang:1.16 AS build
 
 COPY go.mod /go/src/playground/go.mod
 COPY go.sum /go/src/playground/go.sum
diff --git a/server_test.go b/server_test.go
index 7b6f77b..ae3a9cd 100644
--- a/server_test.go
+++ b/server_test.go
@@ -299,37 +299,6 @@
 	}
 }
 
-func TestAllowModuleDownloads(t *testing.T) {
-	const envKey = "ALLOW_PLAY_MODULE_DOWNLOADS"
-	defer func(old string) { os.Setenv(envKey, old) }(os.Getenv(envKey))
-
-	tests := []struct {
-		src  string
-		env  string
-		want bool
-	}{
-		{src: "package main", want: true},
-		{src: "package main", env: "false", want: false},
-		{src: `import "code.google.com/p/go-tour/"`, want: false},
-	}
-	for i, tt := range tests {
-		if tt.env != "" {
-			os.Setenv(envKey, tt.env)
-		} else {
-			os.Setenv(envKey, "true")
-		}
-		files, err := splitFiles([]byte(tt.src))
-		if err != nil {
-			t.Errorf("%d. splitFiles = %v", i, err)
-			continue
-		}
-		got := allowModuleDownloads(files)
-		if got != tt.want {
-			t.Errorf("%d. allow = %v; want %v; files:\n%s", i, got, tt.want, filesAsString(files))
-		}
-	}
-}
-
 func TestPlaygroundGoproxy(t *testing.T) {
 	const envKey = "PLAY_GOPROXY"
 	defer os.Setenv(envKey, os.Getenv(envKey))
diff --git a/tests.go b/tests.go
index 6c60e62..760c971 100644
--- a/tests.go
+++ b/tests.go
@@ -41,10 +41,6 @@
 		stdlog.Fatal(err)
 	}
 
-	// Enable module downloads for testing:
-	defer func(old string) { os.Setenv("ALLOW_PLAY_MODULE_DOWNLOADS", old) }(os.Getenv("ALLOW_PLAY_MODULE_DOWNLOADS"))
-	os.Setenv("ALLOW_PLAY_MODULE_DOWNLOADS", "true")
-
 	failed := false
 	for i, t := range tests {
 		stdlog.Printf("testing case %d (%q)...\n", i, t.name)
@@ -178,30 +174,6 @@
 	}
 }
 `, want: "timers fired as expected"},
-
-	{
-		name: "old_tour_pkgs_in_gopath",
-		prog: `
-package main
-
-import (
-	"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"
-)
-
-var (
-	_ = pic.Show
-	_ = reader.Validate
-	_ = tree.New
-	_ = wc.Test
-)
-
-func main() {
-	println("ok")
-}
-`, want: "ok"},
 	{
 		name: "must_be_package_main",
 		prog: `
diff --git a/vet.go b/vet.go
index 032a5aa..905c03a 100644
--- a/vet.go
+++ b/vet.go
@@ -21,7 +21,7 @@
 // Deprecated: this is the handler for the legacy /vet endpoint; use
 // the /compile (compileAndRun) handler instead with the WithVet
 // boolean set. This code path doesn't support modules and only exists
-// as a temporary compatiblity bridge to older javascript clients.
+// as a temporary compatibility bridge to older javascript clients.
 func vetCheck(ctx context.Context, req *request) (*response, error) {
 	tmpDir, err := ioutil.TempDir("", "vet")
 	if err != nil {
@@ -33,8 +33,7 @@
 	if err := ioutil.WriteFile(in, []byte(req.Body), 0400); err != nil {
 		return nil, fmt.Errorf("error creating temp file %q: %v", in, err)
 	}
-	const useModules = false // legacy handler; no modules (see func comment)
-	vetOutput, err := vetCheckInDir(tmpDir, os.Getenv("GOPATH"), useModules)
+	vetOutput, err := vetCheckInDir(tmpDir, os.Getenv("GOPATH"))
 	if err != nil {
 		// This is about errors running vet, not vet returning output.
 		return nil, err
@@ -43,27 +42,21 @@
 }
 
 // vetCheckInDir runs go vet in the provided directory, using the
-// provided GOPATH value, and whether modules are enabled. The
-// returned error is only about whether go vet was able to run, not
-// whether vet reported problem. The returned value is ("", nil) if
-// vet successfully found nothing, and (non-empty, nil) if vet ran and
-// found issues.
-func vetCheckInDir(dir, goPath string, modules bool) (output string, execErr error) {
+// provided GOPATH value. The returned error is only about whether
+// go vet was able to run, not whether vet reported problem. The
+// returned value is ("", nil) if vet successfully found nothing,
+// and (non-empty, nil) if vet ran and found issues.
+func vetCheckInDir(dir, goPath string) (output string, execErr error) {
 	cmd := exec.Command("go", "vet")
-	if !modules {
-		cmd.Args = append(cmd.Args, progName)
-	}
 	cmd.Dir = dir
 	// Linux go binary is not built with CGO_ENABLED=0.
 	// Prevent vet to compile packages in cgo mode.
 	// See #26307.
 	cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GOPATH="+goPath)
-	if modules {
-		cmd.Env = append(cmd.Env,
-			"GO111MODULE=on",
-			"GOPROXY="+playgroundGoproxy(),
-		)
-	}
+	cmd.Env = append(cmd.Env,
+		"GO111MODULE=on",
+		"GOPROXY="+playgroundGoproxy(),
+	)
 	out, err := cmd.CombinedOutput()
 	if err == nil {
 		return "", nil