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

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

Issue 2460803003: impl/prod: Embed AppEngine SDK into Context. (Closed)
Patch Set: Created 4 years, 1 month 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 | « no previous file | impl/prod/context_vm.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"
(...skipping 14 matching lines...) Expand all
25 "https://www.googleapis.com/auth/cloud.platform", 25 "https://www.googleapis.com/auth/cloud.platform",
26 } 26 }
27 27
28 type key int 28 type key int
29 29
30 var ( 30 var (
31 prodStateKey = "contains the current *prodState" 31 prodStateKey = "contains the current *prodState"
32 probeCacheKey = "contains the current *infoProbeCache" 32 probeCacheKey = "contains the current *infoProbeCache"
33 ) 33 )
34 34
35 // AEContext retrieves the raw "google.golang.org/appengine" compatible Context. 35 // getAEContext retrieves the raw "google.golang.org/appengine" compatible
36 // Context.
36 // 37 //
37 // It also transfers deadline of `c` to AE context, since deadline is used for 38 // This is an independent Context chain from `c`. In an attempt to maintain user
38 // RPCs. Doesn't transfer cancelation ability though (since it's ignored by GAE 39 // expectations, the deadline of `c` is transferred to the returned Context,
39 // anyway). 40 // RPCs. Cancelation is not transferred.
40 func AEContext(c context.Context) context.Context { 41 func getAEContext(c context.Context) context.Context {
41 ps := getProdState(c) 42 ps := getProdState(c)
42 return ps.context(c) 43 return ps.context(c)
43 } 44 }
44 45
45 func setupAECtx(c, aeCtx context.Context) context.Context { 46 func setupAECtx(c context.Context) context.Context {
47 » // Retain the base AppEngine Context. This will allow us to switch betwe en
48 » // transactional and non-transactional Contexts as needed.
46 c = withProdState(c, prodState{ 49 c = withProdState(c, prodState{
47 » » ctx: aeCtx, 50 » » ctx: c,
48 » » noTxnCtx: aeCtx, 51 » » noTxnCtx: c,
49 }) 52 })
50 return useModule(useMail(useUser(useURLFetch(useRDS(useMC(useTQ(useGI(us eLogging(c))))))))) 53 return useModule(useMail(useUser(useURLFetch(useRDS(useMC(useTQ(useGI(us eLogging(c)))))))))
51 } 54 }
52 55
53 // Use adds production implementations for all the gae services to the 56 // Use adds production implementations for all the gae services to the
54 // context. 57 // context. The implementations are all backed by the real AppEngine SDK
58 // functionality,
55 // 59 //
56 // The services added are: 60 // The services added are:
57 // - github.com/luci-go/common/logging 61 // - github.com/luci-go/common/logging
58 // - github.com/luci/gae/service/datastore 62 // - github.com/luci/gae/service/datastore
59 // - github.com/luci/gae/service/info 63 // - github.com/luci/gae/service/info
60 // - github.com/luci/gae/service/mail 64 // - github.com/luci/gae/service/mail
61 // - github.com/luci/gae/service/memcache 65 // - github.com/luci/gae/service/memcache
62 // - github.com/luci/gae/service/module 66 // - github.com/luci/gae/service/module
63 // - github.com/luci/gae/service/taskqueue 67 // - github.com/luci/gae/service/taskqueue
64 // - github.com/luci/gae/service/urlfetch 68 // - github.com/luci/gae/service/urlfetch
65 // - github.com/luci/gae/service/user 69 // - github.com/luci/gae/service/user
66 // 70 //
67 // These can be retrieved with the <service>.Get functions. 71 // In addition, Use installs Google AppEngine SDK Context values into the
68 // 72 // returned Context, allowing it to be used with raw Google AppEngine SDK APIs.
69 // The implementations are all backed by the real appengine SDK functionality, 73 // THIS IS VERY DANGEROUS, as the AppEngine SDK is NOT aware of luci/gae
74 // services and DOES NOT coordinate state with them. Dependency on AppEngine SDK
75 // calls undermines a lot of value that luci/gae adds, and should be used
76 // CAUTIOUSLY and EXPERTLY. Some examples of pitfalls:
iannucci 2016/10/28 18:40:05 as I mentioned on the other CL, I would stress tha
77 //» - AppEngine SDK is not aware of the luci/gae transaction state of the
78 //» Context. AppEngine calls to a Context that is in a luci/gae transactio n
79 //» will not operate on that transaction, and luci/gae calls to a Context that
80 //» is in an AppEngine SDK transaction state will not operate on that
81 //» transaction.
82 //» - luci/gae filters are completely bypassed when accessing AppEngine SDK
83 //» directly. This means that operations such as `dscache` will not be
84 //» consulted or updated, leading to potential inconsistent state and memo ry
85 //» corruption issues.
iannucci 2016/10/28 18:40:05 data corruption. not really memory corruption.
70 func Use(c context.Context, r *http.Request) context.Context { 86 func Use(c context.Context, r *http.Request) context.Context {
71 » return setupAECtx(c, appengine.NewContext(r)) 87 » return setupAECtx(appengine.WithContext(c, r))
iannucci 2016/10/28 18:40:05 why is setupAECtx still needed at all?
dnj 2016/10/29 00:10:59 UseRemote still uses it.
72 } 88 }
73 89
74 // UseRemote is the same as Use, except that it lets you attach a context to 90 // UseRemote is the same as Use, except that it lets you attach a context to
75 // a remote host using the Remote API feature. See the docs for the 91 // a remote host using the Remote API feature. See the docs for the
76 // prerequisites. 92 // prerequisites.
77 // 93 //
78 // docs: https://cloud.google.com/appengine/docs/go/tools/remoteapi 94 // docs: https://cloud.google.com/appengine/docs/go/tools/remoteapi
79 // 95 //
80 // inOutCtx will be replaced with the new, derived context, if err is nil, 96 // If client is nil, http.DefaultClient will be used.
81 // otherwise it's unchanged and continues to be safe-to-use. 97 func UseRemote(host string, client *http.Client) (context.Context, error) {
82 //
83 // If client is nil, this will use create a new client, and will try to be
84 // clever about it:
85 // * If you're creating a remote context FROM AppEngine, this will use
86 // urlfetch.Transport. This can be used to allow app-to-app remote_api
87 // control.
88 //
89 // * If host starts with "localhost", this will create a regular http.Client
90 // with a cookiejar, and call the _ah/login API to log in as an admin with
91 // the user "admin@example.com".
92 //
93 // * Otherwise, it will create a Google OAuth2 client with the following scope s:
94 // - "https://www.googleapis.com/auth/appengine.apis"
95 // - "https://www.googleapis.com/auth/userinfo.email"
96 // - "https://www.googleapis.com/auth/cloud.platform"
97 func UseRemote(inOutCtx *context.Context, host string, client *http.Client) (err error) {
98 if client == nil { 98 if client == nil {
99 » » aeCtx := AEContext(*inOutCtx) 99 » » client = http.DefaultClient
100
101 » » if strings.HasPrefix(host, "localhost") {
102 » » » transp := http.DefaultTransport
103 » » » if aeCtx != nil {
104 » » » » transp = urlfetch.Get(*inOutCtx)
105 » » » }
106
107 » » » client = &http.Client{Transport: transp}
108 » » » client.Jar, err = cookiejar.New(nil)
109 » » » if err != nil {
110 » » » » return
111 » » » }
112 » » » u := fmt.Sprintf("http://%s/_ah/login?%s", host, url.Val ues{
113 » » » » "email": {"admin@example.com"},
114 » » » » "admin": {"True"},
115 » » » » "action": {"Login"},
116 » » » }.Encode())
117
118 » » » var rsp *http.Response
119 » » » rsp, err = client.Get(u)
120 » » » if err != nil {
121 » » » » return
122 » » » }
123 » » » defer rsp.Body.Close()
124 » » } else {
125 » » » if aeCtx == nil {
126 » » » » aeCtx = context.Background()
127 » » » }
128 » » » client, err = gOAuth.DefaultClient(aeCtx, RemoteAPIScope s...)
129 » » » if err != nil {
130 » » » » return
131 » » » }
132 » » }
133 } 100 }
134 101
135 » aeCtx, err := remote_api.NewRemoteContext(host, client) 102 » c, err := remote_api.NewRemoteContext(host, client)
136 if err != nil { 103 if err != nil {
137 » » return 104 » » return nil, err
138 } 105 }
139 » *inOutCtx = setupAECtx(*inOutCtx, aeCtx) 106 » return setupAECtx(c), nil
140 » return nil 107 }
108
109 // RemoteAPIClientForHost generates an authenticated HTTP client suitable for
110 // use with UseRemote for the specified host.
111 //
112 //» - If you're creating a remote context FROM AppEngine, this will use
113 //» urlfetch.Transport. This can be used to allow app-to-app remote_api
114 //» control. This is detected when a luci/gae urlfetch service is present in
115 //» the supplied Context.
116 //
117 //» - If host starts with "localhost", this will create a regular http.Clien t
118 //» with a cookiejar, and call the _ah/login API to log in as an admin wit h
119 //» the user "admin@example.com".
120 //
121 //» - Otherwise, it will create a Google OAuth2 client with the following
122 //» scopes:
123 //» - "https://www.googleapis.com/auth/appengine.apis"
124 //» - "https://www.googleapis.com/auth/userinfo.email"
125 //» - "https://www.googleapis.com/auth/cloud.platform"
126 func RemoteAPIClientForHost(c context.Context, host string) (*http.Client, error ) {
127 » aeCtx := getAEContext(c)
128
129 » if strings.HasPrefix(host, "localhost") {
130 » » transp := http.DefaultTransport
131 » » if aeCtx != nil {
132 » » » transp = urlfetch.Get(aeCtx)
133 » » }
134
135 » » client := &http.Client{Transport: transp}
136
137 » » var err error
138 » » if client.Jar, err = cookiejar.New(nil); err != nil {
139 » » » return nil, err
140 » » }
141 » » u := fmt.Sprintf("http://%s/_ah/login?%s", host, url.Values{
142 » » » "email": {"admin@example.com"},
143 » » » "admin": {"True"},
144 » » » "action": {"Login"},
145 » » }.Encode())
146
147 » » var rsp *http.Response
148 » » if rsp, err = client.Get(u); err != nil {
149 » » » return nil, err
150 » » }
151 » » defer rsp.Body.Close()
152 » » return client, nil
153 » }
154
155 » if aeCtx == nil {
156 » » aeCtx = context.Background()
157 » }
158 » return gOAuth.DefaultClient(aeCtx, RemoteAPIScopes...)
141 } 159 }
142 160
143 // prodState is the current production state. 161 // prodState is the current production state.
144 type prodState struct { 162 type prodState struct {
145 // ctx is the current derived GAE context. 163 // ctx is the current derived GAE context.
146 ctx context.Context 164 ctx context.Context
147 165
148 // noTxnCtx is a Context maintained alongside ctx. When a transaction is 166 // noTxnCtx is a Context maintained alongside ctx. When a transaction is
149 // entered, ctx will be updated, but noTxnCtx will not, allowing extra- 167 // entered, ctx will be updated, but noTxnCtx will not, allowing extra-
150 // transactional Context access. 168 // transactional Context access.
(...skipping 26 matching lines...) Expand all
177 aeCtx := ps.ctx 195 aeCtx := ps.ctx
178 if aeCtx == nil { 196 if aeCtx == nil {
179 return nil 197 return nil
180 } 198 }
181 199
182 if deadline, ok := c.Deadline(); ok { 200 if deadline, ok := c.Deadline(); ok {
183 aeCtx, _ = context.WithDeadline(aeCtx, deadline) 201 aeCtx, _ = context.WithDeadline(aeCtx, deadline)
184 } 202 }
185 return aeCtx 203 return aeCtx
186 } 204 }
OLDNEW
« no previous file with comments | « no previous file | impl/prod/context_vm.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698