blob: 6a89350fdb8ea2f137f1886f80dfd7ab876617ee [file] [log] [blame]
// Copyright 2015 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 prod
import (
"context"
"net/http"
"google.golang.org/appengine"
)
var (
prodStateKey = "contains the current *prodState"
probeCacheKey = "contains the current *infoProbeCache"
)
// getAEContext retrieves the raw "google.golang.org/appengine" compatible
// Context.
//
// This is an independent Context chain from `c`. In an attempt to maintain user
// expectations, the deadline of `c` is transferred to the returned Context,
// RPCs. Cancelation is not transferred.
func getAEContext(c context.Context) context.Context {
ps := getProdState(c)
return ps.context(c)
}
func setupAECtx(c, aeCtx context.Context, r *http.Request) context.Context {
c = withProdState(c, prodState{
ctx: aeCtx,
noTxnCtx: aeCtx,
})
return useModule(useMail(useUser(useURLFetch(useRDS(useMC(useTQ(useGI(useLogging(c, r)))))))))
}
// Use adds production implementations for all the gae services to the
// context. The implementations are all backed by the real appengine SDK
// functionality.
//
// The services added are:
// - github.com/luci-go/common/logging
// - go.chromium.org/luci/gae/service/datastore
// - go.chromium.org/luci/gae/service/info
// - go.chromium.org/luci/gae/service/mail
// - go.chromium.org/luci/gae/service/memcache
// - go.chromium.org/luci/gae/service/module
// - go.chromium.org/luci/gae/service/taskqueue
// - go.chromium.org/luci/gae/service/urlfetch
// - go.chromium.org/luci/gae/service/user
//
// These can be retrieved with the <service>.Get functions.
//
// It is important to note that this DOES NOT install the AppEngine SDK into the
// supplied Context. In general, using the raw AppEngine SDK to access a service
// that is covered by luci/gae is dangerous, leading to a number of potential
// pitfalls including inconsistent transaction management and data corruption.
//
// Users who wish to access the raw AppEngine SDK must derive their own
// AppEngine Context at their own risk.
func Use(c context.Context, r *http.Request) context.Context {
return setupAECtx(c, appengine.NewContext(r), r)
}
// prodState is the current production state.
type prodState struct {
// ctx is the current derived GAE context.
ctx context.Context
// noTxnCtx is a Context maintained alongside ctx. When a transaction is
// entered, ctx will be updated, but noTxnCtx will not, allowing extra-
// transactional Context access.
noTxnCtx context.Context
// inTxn if true if this is in a transaction, false otherwise.
inTxn bool
}
func getProdState(c context.Context) prodState {
if v := c.Value(&prodStateKey).(*prodState); v != nil {
return *v
}
return prodState{}
}
func withProdState(c context.Context, ps prodState) context.Context {
return context.WithValue(c, &prodStateKey, &ps)
}
// context returns the current AppEngine-bound Context. Prior to returning,
// the deadline from "c" (if any) is applied.
//
// Note that this does not (currently) apply any other Done state or propagate
// cancellation from "c".
//
// Tracking at:
// https://go.chromium.org/luci/gae/issues/59
func (ps *prodState) context(c context.Context) context.Context {
aeCtx := ps.ctx
if aeCtx == nil {
return nil
}
if deadline, ok := c.Deadline(); ok {
aeCtx, _ = context.WithDeadline(aeCtx, deadline)
}
return aeCtx
}