cmd/tip: make tip.golang.org also serve & redirect beta.golang.org to tip

We're turning down the old (and neglected) beta.golang.org godoc VM.
It's not different enough from tip.golang.org, which is more reliable
than it used to be.

Now beta.golang.org will just redirect to tip.

Updates golang/go#25978
Fixes golang/go#21073

Change-Id: Iae7fe4c82c14cec7381e75a2e214210d38b6e623
Reviewed-on: https://go-review.googlesource.com/c/120056
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
diff --git a/cmd/tip/Dockerfile b/cmd/tip/Dockerfile
index 51cf6d3..bc0374f 100644
--- a/cmd/tip/Dockerfile
+++ b/cmd/tip/Dockerfile
@@ -135,7 +135,8 @@
 
 # golang sets GOPATH=/go
 ADD . /go/src/tip
-RUN go install --tags=autocert tip
+WORKDIR /go/src/tip
+RUN go install --tags=autocert
 ENTRYPOINT ["/go/bin/tip"]
 
 # We listen on 8080 (for historical reasons). The service.yaml maps public port 80 to 8080.
diff --git a/cmd/tip/Makefile b/cmd/tip/Makefile
index 7902688..ac7d937 100644
--- a/cmd/tip/Makefile
+++ b/cmd/tip/Makefile
@@ -2,7 +2,11 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-VERSION=v2
+VERSION ?= $(shell git rev-parse --short HEAD)
+MUTABLE_VERSION ?= latest
+
+IMAGE_STAGING := gcr.io/go-dashboard-dev/tip
+IMAGE_PROD := gcr.io/symbolic-datum-552/tip
 
 .PHONY: usage
 
@@ -14,12 +18,23 @@
 	go install golang.org/x/build/cmd/gitlock
 	gitlock --update=Dockerfile --ignore=NONE --tags=autocert golang.org/x/tools/cmd/tip
 
-docker-prod: Dockerfile
-	docker build -f Dockerfile --tag=gcr.io/symbolic-datum-552/tip:$(VERSION) .
-docker-dev: Dockerfile
-	docker build -f Dockerfile --tag=gcr.io/go-dashboard-dev/tip:$(VERSION) .
+docker-image: Dockerfile *.go
+	docker build --force-rm -f Dockerfile --tag=$(IMAGE_PROD):$(VERSION) .
+	docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_PROD):$(MUTABLE_VERSION)
+	docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_STAGING):$(VERSION)
+	docker tag $(IMAGE_PROD):$(VERSION) $(IMAGE_STAGING):$(MUTABLE_VERSION)
 
-push-prod: docker-prod
-	gcloud docker -- push gcr.io/symbolic-datum-552/tip:$(VERSION)
-push-dev: docker-dev
-	gcloud docker -- push gcr.io/go-dashboard-dev/tip:$(VERSION)
+push-prod: docker-image
+	docker push $(IMAGE_PROD):$(MUTABLE_VERSION)
+	docker push $(IMAGE_PROD):$(VERSION)
+push-staging: docker-image
+	docker push $(IMAGE_STAGING):$(MUTABLE_VERSION)
+	docker push $(IMAGE_STAGING):$(VERSION)
+
+deploy-prod: push-prod
+	go install golang.org/x/build/cmd/xb
+	xb --prod kubectl set image deployment/tip-deployment tip=$(IMAGE_PROD):$(VERSION)
+deploy-staging: push-staging
+	go install golang.org/x/build/cmd/xb
+	xb --staging kubectl set image deployment/tip-deployment tip=$(IMAGE_STAGING):$(VERSION)
+
diff --git a/cmd/tip/cert.go b/cmd/tip/cert.go
index f912cf3..991cb38 100644
--- a/cmd/tip/cert.go
+++ b/cmd/tip/cert.go
@@ -17,6 +17,7 @@
 	"crypto/tls"
 	"log"
 	"net/http"
+	"strings"
 
 	"cloud.google.com/go/storage"
 	"golang.org/x/build/autocertcache"
@@ -42,7 +43,7 @@
 	}
 	autocertManager = &autocert.Manager{
 		Prompt:     autocert.AcceptTOS,
-		HostPolicy: autocert.HostWhitelist(*autoCertDomain),
+		HostPolicy: autocert.HostWhitelist(strings.Split(*autoCertDomain, ",")...),
 		Cache:      cache,
 	}
 }
diff --git a/cmd/tip/tip-rc.yaml b/cmd/tip/tip-prod.yaml
similarity index 70%
rename from cmd/tip/tip-rc.yaml
rename to cmd/tip/tip-prod.yaml
index 82af3b7..ce4ad07 100644
--- a/cmd/tip/tip-rc.yaml
+++ b/cmd/tip/tip-prod.yaml
@@ -1,11 +1,9 @@
-apiVersion: v1
-kind: ReplicationController
+apiVersion: extensions/v1beta1
+kind: Deployment
 metadata:
-  name: tipgodoc
+  name: tipgodoc-deployment
 spec:
   replicas: 1
-  selector:
-    app: tipgodoc
   template:
     metadata:
       name: tipgodoc
@@ -16,10 +14,10 @@
       - name: cache-volume
         emptyDir: {}
       containers:
-      - name: gitmirror
-        image: gcr.io/symbolic-datum-552/tip:v2
+      - name: tipgodoc
+        image: gcr.io/symbolic-datum-552/tip:latest
         imagePullPolicy: Always
-        command: ["/go/bin/tip", "--autocert=tip.golang.org", "--autocert-bucket=golang-tip-autocert"]
+        command: ["/go/bin/tip", "--autocert=tip.golang.org,beta.golang.org", "--autocert-bucket=golang-tip-autocert"]
         env:
         - name: TMPDIR
           value: /build
diff --git a/cmd/tip/tip.go b/cmd/tip/tip.go
index 35b06ec..ce9c4b7 100644
--- a/cmd/tip/tip.go
+++ b/cmd/tip/tip.go
@@ -34,7 +34,7 @@
 var startTime = time.Now()
 
 var (
-	autoCertDomain      = flag.String("autocert", "", "if non-empty, listen on port 443 and serve a LetsEncrypt cert for this hostname")
+	autoCertDomain      = flag.String("autocert", "", "if non-empty, listen on port 443 and serve a LetsEncrypt cert for this hostname or hostnames (comma-separated)")
 	autoCertCacheBucket = flag.String("autocert-bucket", "", "if non-empty, the Google Cloud Storage bucket in which to store the LetsEncrypt cache")
 )
 
@@ -120,6 +120,18 @@
 		p.serveStatus(w, r)
 		return
 	}
+	// Redirect the old beta.golang.org URL to tip.golang.org,
+	// just in case there are old links out there to
+	// beta.golang.org. (We used to run a "temporary" beta.golang.org
+	// GCE VM running godoc where "temporary" lasted two years.
+	// So it lasted so long, there are probably links to it out there.)
+	if r.Host == "beta.golang.org" {
+		u := *r.URL
+		u.Scheme = "https"
+		u.Host = "tip.golang.org"
+		http.Redirect(w, r, u.String(), http.StatusFound)
+		return
+	}
 	p.mu.Lock()
 	proxy := p.proxy
 	err := p.err