1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:54:45 -07:00

cmd/tip: delete

The cmd/tip program moved to x/build (along with its history) in:

  https://go.googlesource.com/build/+/63986c177d1ff5d2629840f6b00c445f1cb932bf

(There's no associated Gerrit CL; the merge commit of x/build's old
HEAD + a git-filter-branch of x/tools's cmd/tip was pushed directly to
Gerrit's git server, without creating Gerrit CLs for review for each
commit in its history)

Updates golang/go#29981

Change-Id: I16b9b1b0079e3d7b6851cb3e7322a878ece73e23
Reviewed-on: https://go-review.googlesource.com/c/160817
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
This commit is contained in:
Brad Fitzpatrick 2019-02-01 18:49:11 +00:00
parent 067a2f313b
commit 0e05534988
11 changed files with 0 additions and 1016 deletions

View File

@ -1,176 +0,0 @@
FROM golang:1.11
RUN apt-get update && apt-get install --no-install-recommends -y -q build-essential git
# For implicit GOCACHE (Issues 29243 and 29251), set HOME:
RUN mkdir -p /home/gopher
ENV HOME /home/gopher
# golang puts its go install here (weird but true)
ENV GOROOT_BOOTSTRAP /usr/local/go
# BEGIN deps (run `make update-deps` to update)
# Repo cloud.google.com/go at b5eca92 (2018-10-23)
ENV REV=b5eca92245a08e245bc29c4880c9779ea4aeaa9a
RUN go get -d cloud.google.com/go/compute/metadata `#and 7 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 b4deda0 (2018-04-30)
ENV REV=b4deda0973fb4c70b50d226b1af49f3da59f5265
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 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 go.opencensus.io at ebd8d31 (2018-05-16)
ENV REV=ebd8d31470fedf6c27d0e3056653ddff642509b8
RUN go get -d go.opencensus.io/exporter/stackdriver/propagation `#and 12 other pkgs` &&\
(cd /go/src/go.opencensus.io && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
# Repo golang.org/x/build at 7b78c20 (2018-12-13)
ENV REV=7b78c2042368d5c56ee9dbd92ab5fa988c763944
RUN go get -d golang.org/x/build/autocertcache &&\
(cd /go/src/golang.org/x/build && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
# Repo golang.org/x/crypto at e4dc69e (2018-11-06)
ENV REV=e4dc69e5b2fd71dcaf8bd5d054eb936deb78d1fa
RUN go get -d golang.org/x/crypto/acme `#and 2 other pkgs` &&\
(cd /go/src/golang.org/x/crypto && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
# Repo golang.org/x/net at 891ebc4 (2018-12-13)
ENV REV=891ebc4b82d6e74f468c533b06f983c7be918a96
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 f42d051 (2018-11-06)
ENV REV=f42d05182288abf10faef86d16c0d07b8d40ea2d
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/sys at 4d1cda0 (2018-12-13)
ENV REV=4d1cda033e0619309c606fc686de3adcf599539e
RUN go get -d golang.org/x/sys/unix &&\
(cd /go/src/golang.org/x/sys && (git cat-file -t $REV 2>/dev/null || git fetch -q origin $REV) && git reset --hard $REV)
# Repo golang.org/x/text at 6f44c5a (2018-10-30)
ENV REV=6f44c5a2ea40ee3593d98cdcc905cc1fdaa660e2
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 google.golang.org/api at 20530fd (2018-05-06)
ENV REV=20530fd5d65ad2caee87891f9896d7547cb400c9
RUN go get -d google.golang.org/api/gensupport `#and 9 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 86e600f (2018-04-27)
ENV REV=86e600f69ee4704c6efbf6a2a40a5c10700e76c2
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 07ef407 (2018-08-06)
ENV REV=07ef407d991f1004e6c3367c8f452ed9a02f17ff
RUN go get -d google.golang.org/grpc `#and 26 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/iam \
cloud.google.com/go/internal \
cloud.google.com/go/internal/optional \
cloud.google.com/go/internal/trace \
cloud.google.com/go/internal/version \
cloud.google.com/go/storage \
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/timestamp \
github.com/googleapis/gax-go \
go.opencensus.io/exporter/stackdriver/propagation \
go.opencensus.io/internal \
go.opencensus.io/internal/tagencoding \
go.opencensus.io/plugin/ochttp \
go.opencensus.io/plugin/ochttp/propagation/b3 \
go.opencensus.io/stats \
go.opencensus.io/stats/internal \
go.opencensus.io/stats/view \
go.opencensus.io/tag \
go.opencensus.io/trace \
go.opencensus.io/trace/internal \
go.opencensus.io/trace/propagation \
golang.org/x/build/autocertcache \
golang.org/x/crypto/acme \
golang.org/x/crypto/acme/autocert \
golang.org/x/net/context \
golang.org/x/net/context/ctxhttp \
golang.org/x/net/http/httpguts \
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/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/sys/unix \
golang.org/x/text/secure/bidirule \
golang.org/x/text/transform \
golang.org/x/text/unicode/bidi \
golang.org/x/text/unicode/norm \
google.golang.org/api/gensupport \
google.golang.org/api/googleapi \
google.golang.org/api/googleapi/internal/uritemplates \
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/storage/v1 \
google.golang.org/api/transport/http \
google.golang.org/genproto/googleapis/api/annotations \
google.golang.org/genproto/googleapis/iam/v1 \
google.golang.org/genproto/googleapis/rpc/code \
google.golang.org/genproto/googleapis/rpc/status \
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/encoding \
google.golang.org/grpc/encoding/proto \
google.golang.org/grpc/grpclog \
google.golang.org/grpc/internal \
google.golang.org/grpc/internal/backoff \
google.golang.org/grpc/internal/channelz \
google.golang.org/grpc/internal/envconfig \
google.golang.org/grpc/internal/grpcrand \
google.golang.org/grpc/internal/transport \
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/passthrough \
google.golang.org/grpc/stats \
google.golang.org/grpc/status \
google.golang.org/grpc/tap
# END deps.
# golang sets GOPATH=/go
ADD . /go/src/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.
# We also listen on 443 for LetsEncrypt TLS.
EXPOSE 8080 443

View File

@ -1,40 +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.
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
usage:
echo "See Makefile"
exit 1
update-deps:
go install golang.org/x/build/cmd/gitlock
gitlock --update=Dockerfile --ignore=NONE --tags=autocert golang.org/x/tools/cmd/tip
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-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/tipgodoc-deployment tipgodoc=$(IMAGE_PROD):$(VERSION)
deploy-staging: push-staging
go install golang.org/x/build/cmd/xb
xb --staging kubectl set image deployment/tipgodoc-deployment tipgodoc=$(IMAGE_STAGING):$(VERSION)

View File

@ -1,28 +0,0 @@
============================================================
Old instructions, only valid for talks.golang.org:
============================================================
1. Deploy the app.
To deploy tip.golang.org:
(See Kubernetes instruction below.)
To deploy talks.golang.org:
$ gcloud --project golang-org app deploy --no-promote talks.yaml
2. Wait until the deployed version is serving requests.
3. Go to the developer console and upgrade the default version.
https://console.developers.google.com/appengine/versions?project=golang-org&moduleId=tip
4. Clean up any old versions (they continue to use at least one instance).
============================================================
New Kubernetes instructions, for tip.golang.org:
============================================================
Kubernetes instructions:
* make deploy-prod
Also: move talks.golang.org to GKE too?

View File

@ -1,64 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build autocert
// This file contains autocert and cloud.google.com/go/storage
// dependencies we want to hide by default from the Go build system,
// which currently doesn't know how to fetch non-golang.org/x/*
// dependencies. The Dockerfile builds the production binary
// with this code using --tags=autocert.
package main
import (
"context"
"crypto/tls"
"log"
"net/http"
"strings"
"cloud.google.com/go/storage"
"golang.org/x/build/autocertcache"
"golang.org/x/crypto/acme/autocert"
)
func init() {
runHTTPS = runHTTPSAutocert
certInit = certInitAutocert
wrapHTTPMux = wrapHTTPMuxAutocert
}
var autocertManager *autocert.Manager
func certInitAutocert() {
var cache autocert.Cache
if b := *autoCertCacheBucket; b != "" {
sc, err := storage.NewClient(context.Background())
if err != nil {
log.Fatalf("storage.NewClient: %v", err)
}
cache = autocertcache.NewGoogleCloudStorageCache(sc, b)
}
autocertManager = &autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist(strings.Split(*autoCertDomain, ",")...),
Cache: cache,
}
}
func runHTTPSAutocert(h http.Handler) error {
s := &http.Server{
Addr: ":https",
Handler: h,
TLSConfig: &tls.Config{
GetCertificate: autocertManager.GetCertificate,
},
}
return s.ListenAndServeTLS("", "")
}
func wrapHTTPMuxAutocert(h http.Handler) http.Handler {
return autocertManager.HTTPHandler(h)
}

View File

@ -1,102 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"path/filepath"
)
type godocBuilder struct{}
func prefix8(s string) string {
if len(s) < 8 {
return s
}
return s[:8]
}
func (b godocBuilder) Signature(heads map[string]string) string {
// x/net is intentionally not a part of the signature, because
// at this time it does not contribute substantially to the deployed
// website, and so we don't want tip.golang.org redeployed whenever
// x/net changes. This will no longer matter when the Go website uses
// modules to pin its dependencies to specific versions.
return fmt.Sprintf("go=%v/tools=%v", prefix8(heads["go"]), prefix8(heads["tools"]))
}
func (b godocBuilder) Init(logger *log.Logger, dir, hostport string, heads map[string]string) (*exec.Cmd, error) {
goDir := filepath.Join(dir, "go")
toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
netDir := filepath.Join(dir, "gopath/src/golang.org/x/net")
logger.Printf("checking out go repo ...")
if err := checkout(repoURL+"go", heads["go"], goDir); err != nil {
return nil, fmt.Errorf("checkout of go: %v", err)
}
logger.Printf("checking out tools repo ...")
if err := checkout(repoURL+"tools", heads["tools"], toolsDir); err != nil {
return nil, fmt.Errorf("checkout of tools: %v", err)
}
logger.Printf("checking out net repo ...")
if err := checkout(repoURL+"net", heads["net"], netDir); err != nil {
return nil, fmt.Errorf("checkout of net: %v", err)
}
var logWriter io.Writer = toLoggerWriter{logger}
make := exec.Command(filepath.Join(goDir, "src/make.bash"))
make.Dir = filepath.Join(goDir, "src")
make.Stdout = logWriter
make.Stderr = logWriter
logger.Printf("running make.bash in %s ...", make.Dir)
if err := make.Run(); err != nil {
return nil, fmt.Errorf("running make.bash: %v", err)
}
logger.Printf("installing godoc ...")
goBin := filepath.Join(goDir, "bin/go")
goPath := filepath.Join(dir, "gopath")
install := exec.Command(goBin, "install", "golang.org/x/tools/cmd/godoc")
install.Stdout = logWriter
install.Stderr = logWriter
install.Env = append(os.Environ(),
"GOROOT="+goDir,
"GOPATH="+goPath,
"GOROOT_BOOTSTRAP="+os.Getenv("GOROOT_BOOTSTRAP"),
)
if err := install.Run(); err != nil {
return nil, fmt.Errorf("go install golang.org/x/tools/cmd/godoc: %v", err)
}
logger.Printf("starting godoc ...")
godocBin := filepath.Join(goPath, "bin/godoc")
godoc := exec.Command(godocBin, "-http="+hostport, "-index", "-index_interval=-1s", "-play")
godoc.Env = append(os.Environ(), "GOROOT="+goDir)
godoc.Stdout = logWriter
godoc.Stderr = logWriter
if err := godoc.Start(); err != nil {
return nil, fmt.Errorf("starting godoc: %v", err)
}
return godoc, nil
}
var indexingMsg = []byte("Indexing in progress: result may be inaccurate")
func (b godocBuilder) HealthCheck(hostport string) error {
body, err := getOK(fmt.Sprintf("http://%v/search?q=FALLTHROUGH", hostport))
if err != nil {
return err
}
if bytes.Contains(body, indexingMsg) {
return errors.New("still indexing")
}
return nil
}

View File

@ -1,74 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"errors"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
)
type talksBuilder struct{}
func (b talksBuilder) Signature(heads map[string]string) string {
return heads["talks"]
}
const talksToolsRev = "8cab8a1319f0be9798e7fe78b15da75e5f94b2e9"
func (b talksBuilder) Init(logger *log.Logger, dir, hostport string, heads map[string]string) (*exec.Cmd, error) {
// TODO: use logger
toolsDir := filepath.Join(dir, "gopath/src/golang.org/x/tools")
if err := checkout(repoURL+"tools", talksToolsRev, toolsDir); err != nil {
return nil, err
}
talksDir := filepath.Join(dir, "gopath/src/golang.org/x/talks")
if err := checkout(repoURL+"talks", heads["talks"], talksDir); err != nil {
return nil, err
}
goDir := os.Getenv("GOROOT_BOOTSTRAP")
if goDir == "" {
goDir = runtime.GOROOT()
}
goBin := filepath.Join(goDir, "bin/go")
goPath := filepath.Join(dir, "gopath")
presentPath := "golang.org/x/tools/cmd/present"
install := exec.Command(goBin, "install", "-tags=appenginevm", presentPath)
install.Env = append(os.Environ(), "GOROOT="+goDir, "GOPATH="+goPath)
if err := runErr(install); err != nil {
return nil, err
}
talksBin := filepath.Join(goPath, "bin/present")
presentSrc := filepath.Join(goPath, "src", presentPath)
present := exec.Command(talksBin, "-http="+hostport, "-base="+presentSrc)
present.Dir = talksDir
// TODO(adg): log this somewhere useful
present.Stdout = os.Stdout
present.Stderr = os.Stderr
if err := present.Start(); err != nil {
return nil, err
}
return present, nil
}
var talksMsg = []byte("Talks - The Go Programming Language")
func (b talksBuilder) HealthCheck(hostport string) error {
body, err := getOK(fmt.Sprintf("http://%v/", hostport))
if err != nil {
return err
}
if !bytes.Contains(body, talksMsg) {
return errors.New("couldn't match string")
}
return nil
}

View File

@ -1,12 +0,0 @@
service: talks
runtime: custom
env: flex
manual_scaling:
instances: 1
env_variables:
TIP_BUILDER: 'talks'
health_check:
enable_health_check: False

View File

@ -1,38 +0,0 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: tipgodoc-deployment
spec:
replicas: 1
template:
metadata:
name: tipgodoc
labels:
app: tipgodoc
spec:
volumes:
- name: cache-volume
emptyDir: {}
containers:
- name: tipgodoc
image: gcr.io/symbolic-datum-552/tip:latest
imagePullPolicy: Always
command: ["/go/bin/tip", "--autocert=tip.golang.org,beta.golang.org", "--autocert-bucket=golang-tip-autocert"]
env:
- name: TMPDIR
value: /build
- name: TIP_BUILDER
value: godoc
volumeMounts:
- mountPath: /build
name: cache-volume
ports:
- containerPort: 8080
- containerPort: 443
resources:
requests:
cpu: "2"
memory: "4Gi"
limits:
cpu: "2"
memory: "8Gi"

View File

@ -1,16 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: tipgodoc
spec:
ports:
- port: 80
targetPort: 8080
name: http
- port: 443
targetPort: 443
name: https
selector:
app: tipgodoc
type: LoadBalancer
loadBalancerIP: 130.211.180.236

View File

@ -1,441 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// Command tip is the tip.golang.org server,
// serving the latest HEAD straight from the Git oven.
package main
import (
"bufio"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
)
const (
repoURL = "https://go.googlesource.com/"
metaURL = "https://go.googlesource.com/?b=master&format=JSON"
startTimeout = 10 * time.Minute
)
var startTime = time.Now()
var (
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")
)
// Hooks that are set non-nil in cert.go if the "autocert" build tag
// is used.
var (
certInit func()
runHTTPS func(http.Handler) error
wrapHTTPMux func(http.Handler) http.Handler
)
func main() {
flag.Parse()
const k = "TIP_BUILDER"
var b Builder
switch os.Getenv(k) {
case "godoc":
b = godocBuilder{}
case "talks":
b = talksBuilder{}
default:
log.Fatalf("Unknown %v value: %q", k, os.Getenv(k))
}
if certInit != nil {
certInit()
}
p := &Proxy{builder: b}
go p.run()
mux := newServeMux(p)
log.Printf("Starting up tip server for builder %q", os.Getenv(k))
errc := make(chan error, 1)
go func() {
var httpMux http.Handler = mux
if wrapHTTPMux != nil {
httpMux = wrapHTTPMux(httpMux)
}
errc <- http.ListenAndServe(":8080", httpMux)
}()
if *autoCertDomain != "" {
if runHTTPS == nil {
errc <- errors.New("can't use --autocert without building binary with the autocert build tag")
} else {
go func() {
errc <- runHTTPS(mux)
}()
}
log.Printf("Listening on port 443 with LetsEncrypt support on domain %q", *autoCertDomain)
}
if err := <-errc; err != nil {
p.stop()
log.Fatal(err)
}
}
// Proxy implements the tip.golang.org server: a reverse-proxy
// that builds and runs godoc instances showing the latest docs.
type Proxy struct {
builder Builder
mu sync.Mutex // protects following fields
proxy http.Handler
cur string // signature of gorepo+toolsrepo
cmd *exec.Cmd // live godoc instance, or nil for none
side string
hostport string // host and port of the live instance
err error
}
type Builder interface {
Signature(heads map[string]string) string
Init(logger *log.Logger, dir, hostport string, heads map[string]string) (*exec.Cmd, error)
HealthCheck(hostport string) error
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/_tipstatus" {
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
p.mu.Unlock()
if proxy == nil {
s := "starting up"
if err != nil {
s = err.Error()
}
http.Error(w, s, http.StatusInternalServerError)
return
}
proxy.ServeHTTP(w, r)
}
func (p *Proxy) serveStatus(w http.ResponseWriter, r *http.Request) {
p.mu.Lock()
defer p.mu.Unlock()
fmt.Fprintf(w, "side=%v\ncurrent=%v\nerror=%v\nuptime=%v\n", p.side, p.cur, p.err, int(time.Since(startTime).Seconds()))
}
func (p *Proxy) serveHealthCheck(w http.ResponseWriter, r *http.Request) {
p.mu.Lock()
defer p.mu.Unlock()
// NOTE: (App Engine only; not GKE) Status 502, 503, 504 are
// the only status codes that signify an unhealthy app. So
// long as this handler returns one of those codes, this
// instance will not be sent any requests.
if p.proxy == nil {
log.Printf("Health check: not ready")
http.Error(w, "Not ready", http.StatusServiceUnavailable)
return
}
if err := p.builder.HealthCheck(p.hostport); err != nil {
log.Printf("Health check failed: %v", err)
http.Error(w, "Health check failed", http.StatusServiceUnavailable)
return
}
io.WriteString(w, "ok")
}
// run runs in its own goroutine.
func (p *Proxy) run() {
p.mu.Lock()
p.side = "a"
p.mu.Unlock()
for {
p.poll()
time.Sleep(30 * time.Second)
}
}
func (p *Proxy) stop() {
p.mu.Lock()
defer p.mu.Unlock()
if p.cmd != nil {
p.cmd.Process.Kill()
}
}
// poll runs from the run loop goroutine.
func (p *Proxy) poll() {
heads := gerritMetaMap()
if heads == nil {
return
}
sig := p.builder.Signature(heads)
p.mu.Lock()
changes := sig != p.cur
curSide := p.side
p.cur = sig
p.mu.Unlock()
if !changes {
return
}
newSide := "b"
if curSide == "b" {
newSide = "a"
}
dir := filepath.Join(os.TempDir(), "tip", newSide)
if err := os.MkdirAll(dir, 0755); err != nil {
p.err = err
return
}
hostport := "localhost:8081"
if newSide == "b" {
hostport = "localhost:8082"
}
logger := log.New(os.Stderr, sig+": ", log.LstdFlags)
cmd, err := p.builder.Init(logger, dir, hostport, heads)
if err != nil {
logger.Printf("Init failed: %v", err)
err = fmt.Errorf("builder.Init: %v", err)
} else {
go func() {
// TODO(adg,bradfitz): be smarter about dead processes
if err := cmd.Wait(); err != nil {
logger.Printf("process in %v exited: %v (%T)", dir, err, err)
if ee, ok := err.(*exec.ExitError); ok {
logger.Printf("ProcessState.Sys() = %v", ee.ProcessState.Sys())
}
}
}()
err = waitReady(p.builder, hostport)
if err != nil {
cmd.Process.Kill()
err = fmt.Errorf("waitReady: %v", err)
}
}
p.mu.Lock()
defer p.mu.Unlock()
if err != nil {
log.Println(err)
p.err = err
return
}
u, err := url.Parse(fmt.Sprintf("http://%v/", hostport))
if err != nil {
err = fmt.Errorf("parsing hostport: %v", err)
log.Println(err)
p.err = err
return
}
p.proxy = httputil.NewSingleHostReverseProxy(u)
p.side = newSide
p.hostport = hostport
if p.cmd != nil {
p.cmd.Process.Kill()
}
p.cmd = cmd
}
func newServeMux(p *Proxy) http.Handler {
mux := http.NewServeMux()
mux.Handle("/", httpsOnlyHandler{p})
mux.HandleFunc("/_ah/health", p.serveHealthCheck)
return mux
}
func waitReady(b Builder, hostport string) error {
var err error
deadline := time.Now().Add(startTimeout)
for time.Now().Before(deadline) {
if err = b.HealthCheck(hostport); err == nil {
return nil
}
time.Sleep(time.Second)
}
return fmt.Errorf("timed out waiting for process at %v: %v", hostport, err)
}
func runErr(cmd *exec.Cmd) error {
out, err := cmd.CombinedOutput()
if err != nil {
if len(out) == 0 {
return err
}
return fmt.Errorf("%s\n%v", out, err)
}
return nil
}
func checkout(repo, hash, path string) error {
// Clone git repo if it doesn't exist.
if _, err := os.Stat(filepath.Join(path, ".git")); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("mkdir: %v", err)
}
if err := runErr(exec.Command("git", "clone", "--depth", "1", repo, path)); err != nil {
return fmt.Errorf("clone: %v", err)
}
} else if err != nil {
return fmt.Errorf("stat .git: %v", err)
}
// Pull down changes and update to hash.
cmd := exec.Command("git", "fetch")
cmd.Dir = path
if err := runErr(cmd); err != nil {
return fmt.Errorf("fetch: %v", err)
}
cmd = exec.Command("git", "reset", "--hard", hash)
cmd.Dir = path
if err := runErr(cmd); err != nil {
return fmt.Errorf("reset: %v", err)
}
cmd = exec.Command("git", "clean", "-d", "-f", "-x")
cmd.Dir = path
if err := runErr(cmd); err != nil {
return fmt.Errorf("clean: %v", err)
}
return nil
}
var timeoutClient = &http.Client{Timeout: 10 * time.Second}
// gerritMetaMap returns the map from repo name (e.g. "go") to its
// latest master hash.
// The returned map is nil on any transient error.
func gerritMetaMap() map[string]string {
res, err := timeoutClient.Get(metaURL)
if err != nil {
log.Printf("Error getting Gerrit meta map: %v", err)
return nil
}
defer res.Body.Close()
defer io.Copy(ioutil.Discard, res.Body) // ensure EOF for keep-alive
if res.StatusCode != 200 {
return nil
}
var meta map[string]struct {
Branches map[string]string
}
br := bufio.NewReader(res.Body)
// For security reasons or something, this URL starts with ")]}'\n" before
// the JSON object. So ignore that.
// Shawn Pearce says it's guaranteed to always be just one line, ending in '\n'.
for {
b, err := br.ReadByte()
if err != nil {
return nil
}
if b == '\n' {
break
}
}
if err := json.NewDecoder(br).Decode(&meta); err != nil {
log.Printf("JSON decoding error from %v: %s", metaURL, err)
return nil
}
m := map[string]string{}
for repo, v := range meta {
if master, ok := v.Branches["master"]; ok {
m[repo] = master
}
}
return m
}
func getOK(url string) (body []byte, err error) {
res, err := timeoutClient.Get(url)
if err != nil {
return nil, err
}
body, err = ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, errors.New(res.Status)
}
return body, nil
}
// httpsOnlyHandler redirects requests to "http://example.com/foo?bar" to
// "https://example.com/foo?bar". It should be used when the server is listening
// for HTTP traffic behind a proxy that terminates TLS traffic, not when the Go
// server is terminating TLS directly.
type httpsOnlyHandler struct {
h http.Handler
}
// isProxiedReq checks whether the server is running behind a proxy that may be
// terminating TLS.
func isProxiedReq(r *http.Request) bool {
if _, ok := r.Header["X-Appengine-Https"]; ok {
return true
}
if _, ok := r.Header["X-Forwarded-Proto"]; ok {
return true
}
return false
}
func (h httpsOnlyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Appengine-Https") == "off" || r.Header.Get("X-Forwarded-Proto") == "http" ||
(!isProxiedReq(r) && r.TLS == nil) {
r.URL.Scheme = "https"
r.URL.Host = r.Host
http.Redirect(w, r, r.URL.String(), http.StatusFound)
return
}
if r.Header.Get("X-Appengine-Https") == "on" || r.Header.Get("X-Forwarded-Proto") == "https" ||
(!isProxiedReq(r) && r.TLS != nil) {
// Only set this header when we're actually in production.
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
}
h.h.ServeHTTP(w, r)
}
type toLoggerWriter struct{ logger *log.Logger }
func (w toLoggerWriter) Write(p []byte) (int, error) {
w.logger.Printf("%s", p)
return len(p), nil
}

View File

@ -1,25 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package main
import (
"net/http/httptest"
"testing"
)
func TestTipRedirects(t *testing.T) {
mux := newServeMux(&Proxy{builder: &godocBuilder{}})
req := httptest.NewRequest("GET", "http://example.com/foo?bar=baz", nil)
req.Header.Set("X-Forwarded-Proto", "http")
w := httptest.NewRecorder()
mux.ServeHTTP(w, req)
if w.Code != 302 {
t.Errorf("expected Code to be 302, got %d", w.Code)
}
want := "https://example.com/foo?bar=baz"
if loc := w.Header().Get("Location"); loc != want {
t.Errorf("Location header: got %s, want %s", loc, want)
}
}