blob: fc207829899c695aa52ab3ee874d6c1799af6f2b [file] [log] [blame]
// Copyright 2017 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package standard exposes a gaemiddleware Environment for Classic AppEngine.
package standard
import (
"context"
"net/http"
"strings"
"google.golang.org/appengine"
"go.chromium.org/luci/common/tsmon/monitor"
"go.chromium.org/luci/common/tsmon/target"
"go.chromium.org/luci/config/appengine/gaeconfig"
"go.chromium.org/luci/config/validation"
"go.chromium.org/luci/server/auth"
"go.chromium.org/luci/server/auth/authdb"
"go.chromium.org/luci/server/middleware"
"go.chromium.org/luci/server/portal"
"go.chromium.org/luci/server/pprof"
"go.chromium.org/luci/server/router"
"go.chromium.org/luci/server/tsmon"
"go.chromium.org/luci/appengine/gaeauth/client"
gaeauth "go.chromium.org/luci/appengine/gaeauth/server"
"go.chromium.org/luci/appengine/gaeauth/server/gaesigner"
"go.chromium.org/luci/appengine/gaemiddleware"
gaetsmon "go.chromium.org/luci/appengine/tsmon"
"go.chromium.org/luci/gae/impl/prod"
"go.chromium.org/luci/gae/service/info"
)
var (
// globalAuthConfig is configuration of the server/auth library.
//
// It specifies concrete GAE-based implementations for various interfaces
// used by the library.
//
// It is indirectly stateful (since NewDBCache returns a stateful object that
// keeps AuthDB cache in local memory), and thus it's defined as a long living
// global variable.
//
// Used in prod contexts only.
globalAuthConfig = auth.Config{
DBProvider: authdb.NewDBCache(gaeauth.GetAuthDB),
Signer: gaesigner.Signer{},
AccessTokenProvider: client.GetAccessToken,
AnonymousTransport: func(ctx context.Context) http.RoundTripper {
return &contextAwareURLFetch{ctx}
},
FrontendClientID: gaeauth.FetchFrontendClientID,
IsDevMode: appengine.IsDevAppServer(),
}
// globalTsMonState holds configuration and state related to time series
// monitoring.
globalTsMonState = &tsmon.State{
CustomMonitor: tsmonDebugMonitor(),
Target: func(ctx context.Context) target.Task {
return target.Task{
DataCenter: "appengine",
ServiceName: info.AppID(ctx),
JobName: info.ModuleName(ctx),
HostName: strings.SplitN(info.VersionID(ctx), ".", 2)[0],
}
},
InstanceID: info.InstanceID,
TaskNumAllocator: gaetsmon.DatastoreTaskNumAllocator{},
FlushInMiddleware: true,
}
)
// tsmonDebugMonitor returns debug monitor when running on dev server.
func tsmonDebugMonitor() monitor.Monitor {
if appengine.IsDevAppServer() {
return monitor.NewDebugMonitor("")
}
return nil
}
// classicEnv is an AppEngine Classic GAE environment configuration. This is the
// default AppEngine environment for simple (all-classic) layouts.
var classicEnv = gaemiddleware.Environment{
MemcacheAvailable: true,
WithInitialRequest: prod.Use,
WithConfig: gaeconfig.Use,
WithAuth: func(ctx context.Context) context.Context {
return auth.Initialize(ctx, &globalAuthConfig)
},
ExtraMiddleware: func() router.MiddlewareChain {
mw := make([]router.Middleware, 0, 2)
if !appengine.IsDevAppServer() {
mw = append(mw, middleware.WithPanicCatcher)
}
mw = append(mw, globalTsMonState.Middleware)
return router.NewMiddlewareChain(mw...)
}(),
ExtraHandlers: func(r *router.Router, base router.MiddlewareChain) {
gaeauth.InstallHandlers(r, base)
gaetsmon.InstallHandlers(r, base)
portal.InstallHandlers(r, base, &gaeauth.UsersAPIAuthMethod{})
gaeconfig.InstallValidationHandlers(r, base, &validation.Rules)
pprof.InstallHandlers(r, base)
},
}
// With adds various production GAE LUCI services to the context.
//
// Basically, it installs GAE-specific backends and caches for various
// subsystems to make them work in GAE environment.
//
// One example is a backend for Logging: go.chromium.org/luci/common/logging.
// Logs emitted through a With() context go to GAE logs.
//
// 'Production' here means the services will use real GAE APIs (not mocks or
// stubs), so With should never be used from unit tests.
func With(ctx context.Context, req *http.Request) context.Context {
return classicEnv.With(ctx, req)
}
// Base returns a middleware chain to use for all GAE requests.
//
// This middleware chain installs prod GAE services into the request context
// (via With), and wraps the request with a panic catcher and monitoring
// hooks.
func Base() router.MiddlewareChain { return classicEnv.Base() }
// InstallHandlers installs handlers for framework routes using classic
// production services' default middleware.
//
// See InstallHandlersWithMiddleware for more information.
func InstallHandlers(r *router.Router) { classicEnv.InstallHandlers(r) }
// InstallHandlersWithMiddleware installs handlers for framework routes using
// classic production services.
//
// These routes are needed for various services provided in Base context to
// work:
// * Authentication related routes (gaeauth)
// * Settings pages (gaesettings)
// * Various housekeeping crons (tsmon, gaeconfig)
// * Warmup (warmup)
//
// They must be installed into a default module, but it is also safe to
// install them into a non-default module. This may be handy if you want to
// move cron handlers into a non-default module.
//
// 'base' is expected to be an Environment's Base() or its derivative. It must
// NOT do any interception of requests (e.g. checking and rejecting
// unauthenticated requests).
func InstallHandlersWithMiddleware(r *router.Router, base router.MiddlewareChain) {
classicEnv.InstallHandlersWithMiddleware(r, base)
}