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

Side by Side Diff: appengine/gaeconfig/default.go

Issue 2575383002: Add server/cache support to gaeconfig. (Closed)
Patch Set: Created 4 years 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
OLDNEW
1 // Copyright 2016 The LUCI Authors. All rights reserved. 1 // Copyright 2016 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 gaeconfig 5 package gaeconfig
6 6
7 import ( 7 import (
8 "errors" 8 "errors"
9 "fmt" 9 "fmt"
10 "net/http"
11 "os" 10 "os"
12 "path/filepath" 11 "path/filepath"
13 "sync"
14 "time" 12 "time"
15 13
16 » "golang.org/x/net/context" 14 » "github.com/luci/luci-go/common/config/impl/filesystem"
15 » "github.com/luci/luci-go/server/config"
16 » "github.com/luci/luci-go/server/config/caching"
17 » "github.com/luci/luci-go/server/config/erroring"
18 » "github.com/luci/luci-go/server/config/testconfig"
19 » "github.com/luci/luci-go/server/config/textproto"
17 20
18 "github.com/luci/gae/service/info" 21 "github.com/luci/gae/service/info"
19 » "github.com/luci/luci-go/common/config" 22
20 » "github.com/luci/luci-go/common/config/impl/erroring" 23 » "golang.org/x/net/context"
21 » "github.com/luci/luci-go/common/config/impl/filesystem"
22 » "github.com/luci/luci-go/common/config/impl/remote"
23 » "github.com/luci/luci-go/server/auth"
24 ) 24 )
25 25
26 // ErrNotConfigured is returned by methods of config.Interface object returned 26 // ErrNotConfigured is returned by methods of config.Backend object returned
27 // by New if config service URL is not set. Usually happens for new apps. 27 // by New if config service URL is not set. Usually happens for new apps.
28 var ErrNotConfigured = errors.New("config service URL is not set in settings") 28 var ErrNotConfigured = errors.New("config service URL is not set in settings")
29 29
30 // devCfgDir is a name of the directory with config files when running in 30 // devCfgDir is a name of the directory with config files when running in
31 // local dev appserver model. See New for details. 31 // local dev appserver model. See New for details.
32 const devCfgDir = "devcfg" 32 const devCfgDir = "devcfg"
33 33
34 // implCache is used to avoid reallocating config.Interface implementation 34 // Use installs the default luci-config client.
35 // during each request.
36 //
37 // config.Interface instances don't actually depend on the context and thus we
38 // can share them between requests.
39 //
40 // Cache key is the current value of Settings struct. We don't bother to clean
41 // up old entries, since in most cases there'll be none (the settings are mostly
42 // static).
43 var implCache struct {
44 » lock sync.RWMutex
45 » cache map[Settings]config.Interface
46 }
47
48 // New constructs default luci-config client.
49 // 35 //
50 // The client is configured to use luci-config URL specified in the settings, 36 // The client is configured to use luci-config URL specified in the settings,
51 // using GAE app service account for authentication. 37 // using GAE app service account for authentication.
52 // 38 //
53 // If running in prod, and the settings don't specify luci-config URL, produces 39 // If running in prod, and the settings don't specify luci-config URL, produces
54 // an implementation of config.Interface that returns ErrNotConfigured from all 40 // an implementation of config.Interface that returns ErrNotConfigured from all
55 // methods. 41 // methods.
56 // 42 //
57 // If running on devserver, and the settings don't specify luci-config URL, 43 // If running on devserver, and the settings don't specify luci-config URL,
58 // returns a filesystem-based implementation that reads configs from a directory 44 // returns a filesystem-based implementation that reads configs from a directory
59 // (or a symlink) named 'devcfg' located in the GAE module directory (where 45 // (or a symlink) named 'devcfg' located in the GAE module directory (where
60 // app.yaml is) or its immediate parent directory. 46 // app.yaml is) or its immediate parent directory.
61 // 47 //
62 // If such directory can not be located, produces an implementation of 48 // If such directory can not be located, produces an implementation of
63 // config.Interface that returns errors from all methods. 49 // config.Interface that returns errors from all methods.
64 // 50 //
65 // Panics if it can't load the settings (should not happen since they are in 51 // Panics if it can't load the settings (should not happen since they are in
66 // the local memory cache usually). 52 // the local memory cache usually).
67 func New(c context.Context) config.Interface { 53 func Use(c context.Context) context.Context { return useImpl(c, nil) }
iannucci 2017/01/07 21:05:26 if you do the package renaming, this should move o
dnj 2017/01/10 03:30:07 I'll move to luci_config/appengine/gaeconfig.
68 » settings, err := FetchCachedSettings(c) 54
69 » if err != nil { 55 func useImpl(c context.Context, backend config.Backend) context.Context {
70 » » panic(err) 56 » return installConfigBackend(c, mustFetchCachedSettings(c), backend)
57 }
58
59 func installConfigBackend(c context.Context, s *Settings, backend config.Backend ) context.Context {
60 » if backend == nil {
61 » » // Non-testing, build a Backend.
62 » » backend = getPrimaryBackend(c, s)
71 } 63 }
72 64
65 // Install a FormatRegistry. Register common config service protobufs wi th it.
66 c = withFormatRegistry(c, defaultFormatterRegistry())
67
68 backend = &config.FormatBackend{
69 Backend: backend,
70 GetRegistry: GetFormatterRegistry,
71 }
72
73 // Apply caching configuration.
74 exp := time.Duration(s.CacheExpirationSec) * time.Second
75 if exp > 0 {
76 // Add a ProcCache, backed by memcache.
77 backend = caching.ProcCache(backend, exp)
78 backend = memcacheBackend(backend, exp)
79 }
80
81 c = config.WithBackend(c, backend)
82 return c
83 }
84
85 func getPrimaryBackend(c context.Context, settings *Settings) (backend config.Ba ckend) {
86 // Identify our config service Backend (in testing, it will be supplied) .
73 if settings.ConfigServiceURL == "" { 87 if settings.ConfigServiceURL == "" {
74 if info.IsDevAppServer(c) { 88 if info.IsDevAppServer(c) {
75 » » » return devServerConfig() 89 » » » return devServerBackend()
76 } 90 }
77 return erroring.New(ErrNotConfigured) 91 return erroring.New(ErrNotConfigured)
78 } 92 }
79 93 » return &config.ClientBackend{&config.RemoteClientProvider{
80 » return configImplForSettings(settings) 94 » » BaseURL: settings.ConfigServiceURL,
95 » }}
81 } 96 }
82 97
83 // configImplForSettings returns config.Interface based on given settings. 98 func defaultFormatterRegistry() *config.FormatterRegistry {
84 // 99 » var fr config.FormatterRegistry
85 // Split out from New to enforce that returned config.Interface doesn't depend 100 » textproto.RegisterFormatter(&fr)
86 // on the context (since this function doesn't accept a context). 101 » return &fr
87 func configImplForSettings(settings Settings) config.Interface {
88 » implCache.lock.RLock()
89 » impl, ok := implCache.cache[settings]
90 » implCache.lock.RUnlock()
91 » if ok {
92 » » return impl
93 » }
94
95 » implCache.lock.Lock()
96 » defer implCache.lock.Unlock()
97
98 » if impl, ok := implCache.cache[settings]; ok {
99 » » return impl
100 » }
101
102 » impl = remote.New(settings.ConfigServiceURL+"/_ah/api/config/v1/", authe nticatedClient)
103 » if settings.CacheExpirationSec != 0 {
104 » » impl = WrapWithCache(impl, time.Duration(settings.CacheExpiratio nSec)*time.Second)
105 » }
106
107 » if implCache.cache == nil {
108 » » implCache.cache = make(map[Settings]config.Interface, 1)
109 » }
110 » implCache.cache[settings] = impl
111
112 » return impl
113 } 102 }
114 103
115 // devServerConfig returns config.Interface to use on a devserver. 104 // devServerConfig returns config.Interface to use on a devserver.
116 // 105 //
117 // See New for details. 106 // See New for details.
118 func devServerConfig() config.Interface { 107 func devServerBackend() config.Backend {
119 pwd := os.Getenv("PWD") // os.Getwd works funny with symlinks, use PWD 108 pwd := os.Getenv("PWD") // os.Getwd works funny with symlinks, use PWD
120 candidates := []string{ 109 candidates := []string{
121 filepath.Join(pwd, devCfgDir), 110 filepath.Join(pwd, devCfgDir),
122 filepath.Join(filepath.Dir(pwd), devCfgDir), 111 filepath.Join(filepath.Dir(pwd), devCfgDir),
123 } 112 }
124 for _, dir := range candidates { 113 for _, dir := range candidates {
125 if _, err := os.Stat(dir); err == nil { 114 if _, err := os.Stat(dir); err == nil {
126 fs, err := filesystem.New(dir) 115 fs, err := filesystem.New(dir)
127 if err != nil { 116 if err != nil {
128 return erroring.New(err) 117 return erroring.New(err)
129 } 118 }
130 » » » return fs 119 » » » return &config.ClientBackend{&testconfig.LocalClientProv ider{
120 » » » » Base: fs,
121 » » » }}
131 } 122 }
132 } 123 }
133 return erroring.New(fmt.Errorf("luci-config: could not find local config s in any of %s", candidates)) 124 return erroring.New(fmt.Errorf("luci-config: could not find local config s in any of %s", candidates))
134 } 125 }
135 126
136 // authenticatedClient returns http.Client to use for making authenticated 127 var formatRegistryKey = "github.com/luci/luci-go/appengine/gaeconfig:formatRegis try"
137 // request to the config service. 128
138 // 129 func withFormatRegistry(c context.Context, fr *config.FormatterRegistry) context .Context {
139 // The returned client uses GAE app's service account for authentication. 130 » return context.WithValue(c, &formatRegistryKey, fr)
140 func authenticatedClient(ctx context.Context) (*http.Client, error) {
141 » t, err := auth.GetRPCTransport(ctx, auth.AsSelf)
142 » if err != nil {
143 » » return nil, err
144 » }
145 » return &http.Client{Transport: t}, nil
146 } 131 }
132
133 // GetFormatterRegistry is used to retrieve the config service format registry
134 // from the supplied Context.
135 func GetFormatterRegistry(c context.Context) *config.FormatterRegistry {
136 return c.Value(&formatRegistryKey).(*config.FormatterRegistry)
137 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698