blob: 21057950691152f9cf603df26f3898281bd8c8db [file] [log] [blame]
// Copyright 2019 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 (
"context"
"flag"
"net/http"
"os"
"time"
"cloud.google.com/go/profiler"
"contrib.go.opencensus.io/integrations/ocsql"
"github.com/go-redis/redis/v7"
"golang.org/x/discovery/internal"
"golang.org/x/discovery/internal/config"
"golang.org/x/discovery/internal/database"
"golang.org/x/discovery/internal/dcensus"
"golang.org/x/discovery/internal/frontend"
"golang.org/x/discovery/internal/log"
"golang.org/x/discovery/internal/middleware"
"golang.org/x/discovery/internal/postgres"
"golang.org/x/discovery/internal/proxy"
"golang.org/x/discovery/internal/proxydatasource"
)
var (
staticPath = flag.String("static", "content/static", "path to folder containing static files served")
reloadTemplates = flag.Bool("reload_templates", false, "reload templates on each page load (to be used during development)")
directProxy = flag.String("direct_proxy", "", "if set to a valid URL, uses the module proxy referred to by this URL "+
"as a direct backend, bypassing the database")
)
func main() {
flag.Parse()
ctx := context.Background()
if err := config.Init(ctx); err != nil {
log.Fatal(ctx, err)
}
config.Dump(os.Stderr)
if config.UseProfiler() {
if err := profiler.Start(profiler.Config{}); err != nil {
log.Fatalf(ctx, "profiler.Start: %v", err)
}
}
var ds internal.DataSource
if *directProxy != "" {
proxyClient, err := proxy.New(*directProxy)
if err != nil {
log.Fatal(ctx, err)
}
ds = proxydatasource.New(proxyClient)
} else {
// Wrap the postgres driver with OpenCensus instrumentation.
ocDriver, err := ocsql.Register("postgres", ocsql.WithAllTraceOptions())
if err != nil {
log.Fatalf(ctx, "unable to register the ocsql driver: %v\n", err)
}
ddb, err := database.Open(ocDriver, config.DBConnInfo())
if err != nil {
log.Fatalf(ctx, "database.Open: %v", err)
}
db := postgres.New(ddb)
defer db.Close()
ds = db
}
var haClient *redis.Client
if config.RedisHAHost() != "" {
haClient = redis.NewClient(&redis.Options{
Addr: config.RedisHAHost() + ":" + config.RedisHAPort(),
})
}
server, err := frontend.NewServer(ds, haClient, *staticPath, *reloadTemplates)
if err != nil {
log.Fatalf(ctx, "frontend.NewServer: %v", err)
}
router := dcensus.NewRouter(frontend.TagRoute)
var cacheClient *redis.Client
if config.RedisHost() != "" {
cacheClient = redis.NewClient(&redis.Options{
Addr: config.RedisHost() + ":" + config.RedisPort(),
})
}
server.Install(router.Handle, cacheClient)
views := append(dcensus.ServerViews,
postgres.SearchLatencyDistribution,
postgres.SearchResponseCount,
middleware.CacheResultCount,
middleware.CacheErrorCount,
middleware.QuotaResultCount,
)
if err := dcensus.Init(views...); err != nil {
log.Fatal(ctx, err)
}
// We are not currently forwarding any ports on AppEngine, so serving debug
// information is broken.
if !config.OnAppEngine() {
dcensusServer, err := dcensus.NewServer()
if err != nil {
log.Fatal(ctx, err)
}
go http.ListenAndServe(config.DebugAddr("localhost:8081"), dcensusServer)
}
panicHandler, err := server.PanicHandler()
if err != nil {
log.Fatal(ctx, err)
}
requestLogger := getLogger(ctx)
mw := middleware.Chain(
middleware.RequestLog(requestLogger),
middleware.Quota(config.Quota()),
middleware.SecureHeaders(), // must come before any caching for nonces to work
middleware.LatestVersion(server.LatestVersion), // must come before caching for version badge to work
middleware.Panic(panicHandler),
middleware.Timeout(54*time.Second),
)
addr := config.HostAddr("localhost:8080")
log.Infof(ctx, "Listening on addr %s", addr)
log.Fatal(ctx, http.ListenAndServe(addr, mw(router)))
}
func getLogger(ctx context.Context) middleware.Logger {
if config.OnAppEngine() {
logger, err := log.UseStackdriver(ctx, "frontend-log")
if err != nil {
log.Fatal(ctx, err)
}
return logger
}
return middleware.LocalLogger{}
}