go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/gae/impl/prod/info.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package prod 16 17 import ( 18 "context" 19 "time" 20 21 "google.golang.org/appengine" 22 "google.golang.org/appengine/datastore" 23 24 "go.chromium.org/luci/gae/service/info" 25 ) 26 27 // useGI adds a gae.GlobalInfo implementation to context, accessible 28 // by gae.GetGI(c) 29 func useGI(usrCtx context.Context) context.Context { 30 probeCache := getProbeCache(usrCtx) 31 if probeCache == nil { 32 usrCtx = withProbeCache(usrCtx, probe(getAEContext(usrCtx))) 33 } 34 35 return info.SetFactory(usrCtx, func(ci context.Context) info.RawInterface { 36 return giImpl{ci, getAEContext(ci)} 37 }) 38 } 39 40 type giImpl struct { 41 usrCtx context.Context 42 aeCtx context.Context 43 } 44 45 func (g giImpl) AccessToken(scopes ...string) (token string, expiry time.Time, err error) { 46 return appengine.AccessToken(g.aeCtx, scopes...) 47 } 48 func (g giImpl) AppID() string { 49 return appengine.AppID(g.aeCtx) 50 } 51 func (g giImpl) FullyQualifiedAppID() string { 52 return getProbeCache(g.usrCtx).fqaid 53 } 54 func (g giImpl) GetNamespace() string { 55 return getProbeCache(g.usrCtx).namespace 56 } 57 func (g giImpl) Datacenter() string { 58 return appengine.Datacenter(g.aeCtx) 59 } 60 func (g giImpl) DefaultVersionHostname() string { 61 return appengine.DefaultVersionHostname(g.aeCtx) 62 } 63 func (g giImpl) InstanceID() string { 64 return appengine.InstanceID() 65 } 66 func (g giImpl) IsDevAppServer() bool { 67 return appengine.IsDevAppServer() 68 } 69 func (g giImpl) IsOverQuota(err error) bool { 70 return appengine.IsOverQuota(err) 71 } 72 func (g giImpl) IsTimeoutError(err error) bool { 73 return appengine.IsTimeoutError(err) 74 } 75 func (g giImpl) ModuleHostname(module, version, instance string) (string, error) { 76 return appengine.ModuleHostname(g.aeCtx, module, version, instance) 77 } 78 func (g giImpl) ModuleName() (name string) { 79 return appengine.ModuleName(g.aeCtx) 80 } 81 func (g giImpl) Namespace(namespace string) (context.Context, error) { 82 c := g.usrCtx 83 84 pc := *getProbeCache(c) 85 if pc.namespace == namespace { 86 // Already using this namespace. 87 return c, nil 88 } 89 pc.namespace = namespace 90 91 // Apply the namespace to our retained GAE Contexts. 92 var err error 93 ps := getProdState(c) 94 95 // Apply to current GAE Context. 96 if ps.ctx, err = appengine.Namespace(ps.ctx, namespace); err != nil { 97 return c, err 98 } 99 100 // Apply to non-transactional Context. Since the previous one applied 101 // successfully, this must succeed. 102 ps.noTxnCtx, err = appengine.Namespace(ps.noTxnCtx, namespace) 103 if err != nil { 104 panic(err) 105 } 106 107 // Update our user Context with the new namespace-imbued objects. 108 c = withProbeCache(c, &pc) 109 c = withProdState(c, ps) 110 return c, nil 111 } 112 func (g giImpl) PublicCertificates() ([]info.Certificate, error) { 113 certs, err := appengine.PublicCertificates(g.aeCtx) 114 if err != nil { 115 return nil, err 116 } 117 ret := make([]info.Certificate, len(certs)) 118 for i, c := range certs { 119 ret[i] = info.Certificate(c) 120 } 121 return ret, nil 122 } 123 func (g giImpl) RequestID() string { 124 return appengine.RequestID(g.aeCtx) 125 } 126 func (g giImpl) ServerSoftware() string { 127 return appengine.ServerSoftware() 128 } 129 func (g giImpl) ServiceAccount() (string, error) { 130 if appengine.IsDevAppServer() { 131 // On devserver ServiceAccount returns empty string, but AccessToken works. 132 // We use it to grab developer's email. 133 return developerAccount(g.aeCtx) 134 } 135 return appengine.ServiceAccount(g.aeCtx) 136 } 137 func (g giImpl) SignBytes(bytes []byte) (keyName string, signature []byte, err error) { 138 return appengine.SignBytes(g.aeCtx, bytes) 139 } 140 func (g giImpl) VersionID() string { 141 return appengine.VersionID(g.aeCtx) 142 } 143 144 func (g giImpl) GetTestable() info.Testable { return nil } 145 146 type infoProbeCache struct { 147 namespace string 148 fqaid string 149 } 150 151 func probe(aeCtx context.Context) *infoProbeCache { 152 probeKey := datastore.NewKey(aeCtx, "Kind", "id", 0, nil) 153 ipb := infoProbeCache{ 154 fqaid: probeKey.AppID(), 155 namespace: probeKey.Namespace(), 156 } 157 return &ipb 158 } 159 160 func getProbeCache(c context.Context) *infoProbeCache { 161 if pc, ok := c.Value(&probeCacheKey).(*infoProbeCache); ok { 162 return pc 163 } 164 return nil 165 } 166 167 func withProbeCache(c context.Context, pc *infoProbeCache) context.Context { 168 return context.WithValue(c, &probeCacheKey, pc) 169 }