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

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

Issue 2575383002: Add server/cache support to gaeconfig. (Closed)
Patch Set: Un-collapse. Created 3 years, 11 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 | « appengine/gaeconfig/cache_test.go ('k') | appengine/gaeconfig/doc.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package gaeconfig
6
7 import (
8 "errors"
9 "fmt"
10 "net/http"
11 "os"
12 "path/filepath"
13 "sync"
14 "time"
15
16 "golang.org/x/net/context"
17
18 "github.com/luci/gae/service/info"
19 "github.com/luci/luci-go/common/config"
20 "github.com/luci/luci-go/common/config/impl/erroring"
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 )
25
26 // ErrNotConfigured is returned by methods of config.Interface object returned
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")
29
30 // devCfgDir is a name of the directory with config files when running in
31 // local dev appserver model. See New for details.
32 const devCfgDir = "devcfg"
33
34 // implCache is used to avoid reallocating config.Interface implementation
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 //
50 // The client is configured to use luci-config URL specified in the settings,
51 // using GAE app service account for authentication.
52 //
53 // 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
55 // methods.
56 //
57 // 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
59 // (or a symlink) named 'devcfg' located in the GAE module directory (where
60 // app.yaml is) or its immediate parent directory.
61 //
62 // If such directory can not be located, produces an implementation of
63 // config.Interface that returns errors from all methods.
64 //
65 // Panics if it can't load the settings (should not happen since they are in
66 // the local memory cache usually).
67 func New(c context.Context) config.Interface {
68 settings, err := FetchCachedSettings(c)
69 if err != nil {
70 panic(err)
71 }
72
73 if settings.ConfigServiceURL == "" {
74 if info.IsDevAppServer(c) {
75 return devServerConfig()
76 }
77 return erroring.New(ErrNotConfigured)
78 }
79
80 return configImplForSettings(settings)
81 }
82
83 // configImplForSettings returns config.Interface based on given settings.
84 //
85 // Split out from New to enforce that returned config.Interface doesn't depend
86 // on the context (since this function doesn't accept a context).
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 }
114
115 // devServerConfig returns config.Interface to use on a devserver.
116 //
117 // See New for details.
118 func devServerConfig() config.Interface {
119 pwd := os.Getenv("PWD") // os.Getwd works funny with symlinks, use PWD
120 candidates := []string{
121 filepath.Join(pwd, devCfgDir),
122 filepath.Join(filepath.Dir(pwd), devCfgDir),
123 }
124 for _, dir := range candidates {
125 if _, err := os.Stat(dir); err == nil {
126 fs, err := filesystem.New(dir)
127 if err != nil {
128 return erroring.New(err)
129 }
130 return fs
131 }
132 }
133 return erroring.New(fmt.Errorf("luci-config: could not find local config s in any of %s", candidates))
134 }
135
136 // authenticatedClient returns http.Client to use for making authenticated
137 // request to the config service.
138 //
139 // The returned client uses GAE app's service account for authentication.
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 }
OLDNEW
« no previous file with comments | « appengine/gaeconfig/cache_test.go ('k') | appengine/gaeconfig/doc.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698