| OLD | NEW |
| (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/url" | |
| 11 "strconv" | |
| 12 "time" | |
| 13 | |
| 14 "golang.org/x/net/context" | |
| 15 | |
| 16 "github.com/luci/luci-go/server/settings" | |
| 17 ) | |
| 18 | |
| 19 // DefaultExpire is a reasonable default expiration value. | |
| 20 const DefaultExpire = 10 * time.Minute | |
| 21 | |
| 22 // Settings are stored in the datastore via appengine/gaesettings package. | |
| 23 type Settings struct { | |
| 24 // ConfigServiceURL is URL of luci-config service to fetch configs from. | |
| 25 ConfigServiceURL string `json:"config_service_url"` | |
| 26 | |
| 27 // CacheExpirationSec is how long to hold configs in local cache. | |
| 28 CacheExpirationSec int `json:"cache_expiration_sec"` | |
| 29 } | |
| 30 | |
| 31 // FetchCachedSettings fetches Settings from the settings store. | |
| 32 // | |
| 33 // Uses in-process global cache to avoid hitting datastore often. The cache | |
| 34 // expiration time is 1 min (see gaesettings.expirationTime), meaning | |
| 35 // the instance will refetch settings once a minute (blocking only one unlucky | |
| 36 // request to do so). | |
| 37 // | |
| 38 // Returns errors only if there's no cached value (i.e. it is the first call | |
| 39 // to this function in this process ever) and datastore operation fails. | |
| 40 func FetchCachedSettings(c context.Context) (Settings, error) { | |
| 41 s := Settings{} | |
| 42 switch err := settings.Get(c, settingsKey, &s); err { | |
| 43 case nil: | |
| 44 return s, nil | |
| 45 case settings.ErrNoSettings: | |
| 46 return DefaultSettings(), nil | |
| 47 default: | |
| 48 return Settings{}, err | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 // DefaultSettings returns Settings to use if setting store is empty. | |
| 53 func DefaultSettings() Settings { | |
| 54 return Settings{ | |
| 55 CacheExpirationSec: int(DefaultExpire.Seconds()), | |
| 56 } | |
| 57 } | |
| 58 | |
| 59 //////////////////////////////////////////////////////////////////////////////// | |
| 60 // UI for settings. | |
| 61 | |
| 62 // settingsKey is used internally to identify gaeconfig settings in settings | |
| 63 // store. | |
| 64 const settingsKey = "gaeconfig" | |
| 65 | |
| 66 type settingsUIPage struct { | |
| 67 settings.BaseUIPage | |
| 68 } | |
| 69 | |
| 70 func (settingsUIPage) Title(c context.Context) (string, error) { | |
| 71 return "Configuration service settings", nil | |
| 72 } | |
| 73 | |
| 74 func (settingsUIPage) Fields(c context.Context) ([]settings.UIField, error) { | |
| 75 return []settings.UIField{ | |
| 76 { | |
| 77 ID: "ConfigServiceURL", | |
| 78 Title: "Config service URL", | |
| 79 Type: settings.UIFieldText, | |
| 80 Validator: func(v string) error { | |
| 81 if v != "" { | |
| 82 parsed, err := url.Parse(v) | |
| 83 if err != nil { | |
| 84 return fmt.Errorf("bad URL %q -
%s", v, err) | |
| 85 } | |
| 86 if !parsed.IsAbs() || parsed.Path != ""
{ | |
| 87 return fmt.Errorf("bad URL %q -
must be host root URL", v) | |
| 88 } | |
| 89 if parsed.Scheme != "https" { | |
| 90 return fmt.Errorf("bad URL %q -
expecting https:// scheme", v) | |
| 91 } | |
| 92 } | |
| 93 return nil | |
| 94 }, | |
| 95 Help: `<p>The application may fetch configuration files
stored centrally | |
| 96 in an instance of <a href="https://github.com/luci/luci-py/tree/master/appengine
/config_service">luci-config</a> | |
| 97 service. This is an URL of such service. If you don't know what this is, you | |
| 98 probably don't use it and can keep this setting blank.</p>`, | |
| 99 }, | |
| 100 { | |
| 101 ID: "CacheExpirationSec", | |
| 102 Title: "Cache expiration, sec", | |
| 103 Type: settings.UIFieldText, | |
| 104 Validator: func(v string) error { | |
| 105 if i, err := strconv.Atoi(v); err != nil || i <
0 { | |
| 106 return errors.New("expecting a non-negat
ive integer") | |
| 107 } | |
| 108 return nil | |
| 109 }, | |
| 110 Help: `<p>For better performance configuration files fet
ched from remote | |
| 111 service are cached in memcache for specified amount of time. Set it to 0 to | |
| 112 disable local cache.</p>`, | |
| 113 }, | |
| 114 }, nil | |
| 115 } | |
| 116 | |
| 117 func (settingsUIPage) ReadSettings(c context.Context) (map[string]string, error)
{ | |
| 118 s := DefaultSettings() | |
| 119 err := settings.GetUncached(c, settingsKey, &s) | |
| 120 if err != nil && err != settings.ErrNoSettings { | |
| 121 return nil, err | |
| 122 } | |
| 123 return map[string]string{ | |
| 124 "ConfigServiceURL": s.ConfigServiceURL, | |
| 125 "CacheExpirationSec": strconv.Itoa(s.CacheExpirationSec), | |
| 126 }, nil | |
| 127 } | |
| 128 | |
| 129 func (settingsUIPage) WriteSettings(c context.Context, values map[string]string,
who, why string) error { | |
| 130 modified := Settings{} | |
| 131 modified.ConfigServiceURL = values["ConfigServiceURL"] | |
| 132 | |
| 133 var err error | |
| 134 modified.CacheExpirationSec, err = strconv.Atoi(values["CacheExpirationS
ec"]) | |
| 135 if err != nil { | |
| 136 return err | |
| 137 } | |
| 138 | |
| 139 return settings.SetIfChanged(c, settingsKey, &modified, who, why) | |
| 140 } | |
| 141 | |
| 142 func init() { | |
| 143 settings.RegisterUIPage(settingsKey, settingsUIPage{}) | |
| 144 } | |
| OLD | NEW |