Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: impl/prod/context.go

Issue 2302743002: Interface update, per-method Contexts. (Closed)
Patch Set: Lightning talk licenses. Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « impl/memory/user_test.go ('k') | impl/prod/datastore_key.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The LUCI Authors. All rights reserved. 1 // Copyright 2015 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package prod 5 package prod
6 6
7 import ( 7 import (
8 "fmt" 8 "fmt"
9 "net/http" 9 "net/http"
10 "net/http/cookiejar" 10 "net/http/cookiejar"
11 "net/url" 11 "net/url"
12 "strings" 12 "strings"
13 13
14 "github.com/luci/gae/service/info"
15 "github.com/luci/gae/service/urlfetch" 14 "github.com/luci/gae/service/urlfetch"
16 "golang.org/x/net/context" 15 "golang.org/x/net/context"
17 gOAuth "golang.org/x/oauth2/google" 16 gOAuth "golang.org/x/oauth2/google"
18 "google.golang.org/appengine" 17 "google.golang.org/appengine"
19 "google.golang.org/appengine/remote_api" 18 "google.golang.org/appengine/remote_api"
20 ) 19 )
21 20
22 // RemoteAPIScopes is the set of OAuth2 scopes needed for Remote API access. 21 // RemoteAPIScopes is the set of OAuth2 scopes needed for Remote API access.
23 var RemoteAPIScopes = []string{ 22 var RemoteAPIScopes = []string{
24 "https://www.googleapis.com/auth/appengine.apis", 23 "https://www.googleapis.com/auth/appengine.apis",
25 "https://www.googleapis.com/auth/userinfo.email", 24 "https://www.googleapis.com/auth/userinfo.email",
26 "https://www.googleapis.com/auth/cloud.platform", 25 "https://www.googleapis.com/auth/cloud.platform",
27 } 26 }
28 27
29 type key int 28 type key int
30 29
31 var ( 30 var (
32 » prodContextKey key 31 » prodStateKey = "contains the current *prodState"
33 » prodContextNoTxnKey key = 1 32 » probeCacheKey = "contains the current *infoProbeCache"
34 » probeCacheKey key = 2
35 ) 33 )
36 34
37 // AEContext retrieves the raw "google.golang.org/appengine" compatible Context. 35 // AEContext retrieves the raw "google.golang.org/appengine" compatible Context.
38 // 36 //
39 // It also transfers deadline of `c` to AE context, since deadline is used for 37 // It also transfers deadline of `c` to AE context, since deadline is used for
40 // RPCs. Doesn't transfer cancelation ability though (since it's ignored by GAE 38 // RPCs. Doesn't transfer cancelation ability though (since it's ignored by GAE
41 // anyway). 39 // anyway).
42 func AEContext(c context.Context) context.Context { 40 func AEContext(c context.Context) context.Context {
43 » aeCtx, _ := c.Value(prodContextKey).(context.Context) 41 » ps := getProdState(c)
44 » if aeCtx == nil { 42 » return ps.context(c)
45 » » return nil
46 » }
47 » if deadline, ok := c.Deadline(); ok {
48 » » aeCtx, _ = context.WithDeadline(aeCtx, deadline)
49 » }
50 » return aeCtx
51 }
52
53 // AEContextNoTxn retrieves the raw "google.golang.org/appengine" compatible
54 // Context that's not part of a transaction.
55 func AEContextNoTxn(c context.Context) context.Context {
56 » aeCtx, _ := c.Value(prodContextNoTxnKey).(context.Context)
57 » if aeCtx == nil {
58 » » return nil
59 » }
60
61 » if ns, has := info.Get(c).GetNamespace(); has {
62 » » var err error
63 » » aeCtx, err = appengine.Namespace(aeCtx, ns)
64 » » if err != nil {
65 » » » panic(err)
66 » » }
67 » }
68 » if deadline, ok := c.Deadline(); ok {
69 » » aeCtx, _ = context.WithDeadline(aeCtx, deadline)
70 » }
71 » return aeCtx
72 } 43 }
73 44
74 func setupAECtx(c, aeCtx context.Context) context.Context { 45 func setupAECtx(c, aeCtx context.Context) context.Context {
75 » c = context.WithValue(c, prodContextKey, aeCtx) 46 » c = withProdState(c, prodState{
76 » c = context.WithValue(c, prodContextNoTxnKey, aeCtx) 47 » » ctx: aeCtx,
48 » » noTxnCtx: aeCtx,
49 » })
77 return useModule(useMail(useUser(useURLFetch(useRDS(useMC(useTQ(useGI(us eLogging(c))))))))) 50 return useModule(useMail(useUser(useURLFetch(useRDS(useMC(useTQ(useGI(us eLogging(c)))))))))
78 } 51 }
79 52
80 // Use adds production implementations for all the gae services to the 53 // Use adds production implementations for all the gae services to the
81 // context. 54 // context.
82 // 55 //
83 // The services added are: 56 // The services added are:
84 // - github.com/luci-go/common/logging 57 // - github.com/luci-go/common/logging
85 // - github.com/luci/gae/service/datastore 58 // - github.com/luci/gae/service/datastore
86 // - github.com/luci/gae/service/info 59 // - github.com/luci/gae/service/info
(...skipping 29 matching lines...) Expand all
116 // * If host starts with "localhost", this will create a regular http.Client 89 // * If host starts with "localhost", this will create a regular http.Client
117 // with a cookiejar, and call the _ah/login API to log in as an admin with 90 // with a cookiejar, and call the _ah/login API to log in as an admin with
118 // the user "admin@example.com". 91 // the user "admin@example.com".
119 // 92 //
120 // * Otherwise, it will create a Google OAuth2 client with the following scope s: 93 // * Otherwise, it will create a Google OAuth2 client with the following scope s:
121 // - "https://www.googleapis.com/auth/appengine.apis" 94 // - "https://www.googleapis.com/auth/appengine.apis"
122 // - "https://www.googleapis.com/auth/userinfo.email" 95 // - "https://www.googleapis.com/auth/userinfo.email"
123 // - "https://www.googleapis.com/auth/cloud.platform" 96 // - "https://www.googleapis.com/auth/cloud.platform"
124 func UseRemote(inOutCtx *context.Context, host string, client *http.Client) (err error) { 97 func UseRemote(inOutCtx *context.Context, host string, client *http.Client) (err error) {
125 if client == nil { 98 if client == nil {
99 aeCtx := AEContext(*inOutCtx)
100
126 if strings.HasPrefix(host, "localhost") { 101 if strings.HasPrefix(host, "localhost") {
127 transp := http.DefaultTransport 102 transp := http.DefaultTransport
128 » » » if aeCtx := AEContextNoTxn(*inOutCtx); aeCtx != nil { 103 » » » if aeCtx != nil {
129 transp = urlfetch.Get(*inOutCtx) 104 transp = urlfetch.Get(*inOutCtx)
130 } 105 }
131 106
132 client = &http.Client{Transport: transp} 107 client = &http.Client{Transport: transp}
133 client.Jar, err = cookiejar.New(nil) 108 client.Jar, err = cookiejar.New(nil)
134 if err != nil { 109 if err != nil {
135 return 110 return
136 } 111 }
137 u := fmt.Sprintf("http://%s/_ah/login?%s", host, url.Val ues{ 112 u := fmt.Sprintf("http://%s/_ah/login?%s", host, url.Val ues{
138 "email": {"admin@example.com"}, 113 "email": {"admin@example.com"},
139 "admin": {"True"}, 114 "admin": {"True"},
140 "action": {"Login"}, 115 "action": {"Login"},
141 }.Encode()) 116 }.Encode())
142 117
143 var rsp *http.Response 118 var rsp *http.Response
144 rsp, err = client.Get(u) 119 rsp, err = client.Get(u)
145 if err != nil { 120 if err != nil {
146 return 121 return
147 } 122 }
148 defer rsp.Body.Close() 123 defer rsp.Body.Close()
149 } else { 124 } else {
150 aeCtx := AEContextNoTxn(*inOutCtx)
151 if aeCtx == nil { 125 if aeCtx == nil {
152 aeCtx = context.Background() 126 aeCtx = context.Background()
153 } 127 }
154 client, err = gOAuth.DefaultClient(aeCtx, RemoteAPIScope s...) 128 client, err = gOAuth.DefaultClient(aeCtx, RemoteAPIScope s...)
155 if err != nil { 129 if err != nil {
156 return 130 return
157 } 131 }
158 } 132 }
159 } 133 }
160 134
161 aeCtx, err := remote_api.NewRemoteContext(host, client) 135 aeCtx, err := remote_api.NewRemoteContext(host, client)
162 if err != nil { 136 if err != nil {
163 return 137 return
164 } 138 }
165 *inOutCtx = setupAECtx(*inOutCtx, aeCtx) 139 *inOutCtx = setupAECtx(*inOutCtx, aeCtx)
166 return nil 140 return nil
167 } 141 }
142
143 // prodState is the current production state.
144 type prodState struct {
145 // ctx is the current derived GAE context.
146 ctx context.Context
147
148 // noTxnCtx is a Context maintained alongside ctx. When a transaction is
149 // entered, ctx will be updated, but noTxnCtx will not, allowing extra-
150 // transactional Context access.
151 noTxnCtx context.Context
152
153 // inTxn if true if this is in a transaction, false otherwise.
154 inTxn bool
155 }
156
157 func getProdState(c context.Context) prodState {
158 if v := c.Value(&prodStateKey).(*prodState); v != nil {
159 return *v
160 }
161 return prodState{}
162 }
163
164 func withProdState(c context.Context, ps prodState) context.Context {
165 return context.WithValue(c, &prodStateKey, &ps)
166 }
167
168 // context returns the current AppEngine-bound Context. Prior to returning,
169 // the deadline from "c" (if any) is applied.
170 //
171 // Note that this does not (currently) apply any other Done state or propagate
172 // cancellation from "c".
173 //
174 // Tracking at:
175 // https://github.com/luci/gae/issues/59
176 func (ps *prodState) context(c context.Context) context.Context {
177 aeCtx := ps.ctx
178 if aeCtx == nil {
179 return nil
180 }
181
182 if deadline, ok := c.Deadline(); ok {
183 aeCtx, _ = context.WithDeadline(aeCtx, deadline)
184 }
185 return aeCtx
186 }
OLDNEW
« no previous file with comments | « impl/memory/user_test.go ('k') | impl/prod/datastore_key.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698