OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package config | 5 package config |
6 | 6 |
7 import ( | 7 import ( |
| 8 "errors" |
| 9 "fmt" |
8 "time" | 10 "time" |
9 | 11 |
10 "github.com/golang/protobuf/proto" | 12 "github.com/golang/protobuf/proto" |
11 "github.com/luci/luci-go/common/clock" | 13 "github.com/luci/luci-go/common/clock" |
12 "github.com/luci/luci-go/common/config" | 14 "github.com/luci/luci-go/common/config" |
13 log "github.com/luci/luci-go/common/logging" | 15 log "github.com/luci/luci-go/common/logging" |
14 "github.com/luci/luci-go/common/proto/logdog/svcconfig" | 16 "github.com/luci/luci-go/common/proto/logdog/svcconfig" |
| 17 "github.com/luci/luci-go/server/proccache" |
15 "golang.org/x/net/context" | 18 "golang.org/x/net/context" |
16 ) | 19 ) |
17 | 20 |
18 // Options is the set of options used to set up a Manager. | 21 // Options is the set of options used to set up a Manager. |
19 // | 22 // |
20 // The configuration is loaded from a svcconfig.Config protobuf. | 23 // The configuration is loaded from a svcconfig.Config protobuf. |
21 type Options struct { | 24 type Options struct { |
22 // Config is the configuration service to load from. | 25 // Config is the configuration service to load from. |
23 Config config.Interface | 26 Config config.Interface |
24 | 27 |
| 28 // ServiceID is used to load project configurations, which are named aft
er a |
| 29 // specific service. |
| 30 // |
| 31 // If empty, project configurations cannot be loaded. |
| 32 ServiceID string |
| 33 |
25 // ConfigSet is the name of the ConfigSet to load. | 34 // ConfigSet is the name of the ConfigSet to load. |
26 ConfigSet string | 35 ConfigSet string |
27 // ServiceConfigPath is the name of the LogDog service config within the | 36 // ServiceConfigPath is the name of the LogDog service config within the |
28 // ConfigSet. | 37 // ConfigSet. |
29 ServiceConfigPath string | 38 ServiceConfigPath string |
30 | 39 |
| 40 // ProjectConfigCacheDuration is the amount of time to cache a project's |
| 41 // configuration. If this is <= 0, the project config will be fetched ea
ch |
| 42 // time it's requested. |
| 43 ProjectConfigCacheDuration time.Duration |
| 44 |
31 // KillCheckInterval, if >0, starts a goroutine that polls every interva
l to | 45 // KillCheckInterval, if >0, starts a goroutine that polls every interva
l to |
32 // see if the configuration has changed. If it has, KillFunc will be inv
oked. | 46 // see if the configuration has changed. If it has, KillFunc will be inv
oked. |
33 KillCheckInterval time.Duration | 47 KillCheckInterval time.Duration |
34 // KillFunc is the function that will be called if a configuration hash
change | 48 // KillFunc is the function that will be called if a configuration hash
change |
35 // has been observed. | 49 // has been observed. |
36 KillFunc func() | 50 KillFunc func() |
37 } | 51 } |
38 | 52 |
39 func (o *Options) getConfig(hashOnly bool) (*config.Config, error) { | 53 func (o *Options) getConfig(hashOnly bool) (*config.Config, error) { |
40 return o.Config.GetConfig(o.ConfigSet, o.ServiceConfigPath, hashOnly) | 54 return o.Config.GetConfig(o.ConfigSet, o.ServiceConfigPath, hashOnly) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 // It can also periodically refresh that configuration and invoke a shutdown | 98 // It can also periodically refresh that configuration and invoke a shutdown |
85 // function if its content changes. | 99 // function if its content changes. |
86 type Manager struct { | 100 type Manager struct { |
87 o *Options | 101 o *Options |
88 | 102 |
89 // cfg is the initial configuration. | 103 // cfg is the initial configuration. |
90 cfg svcconfig.Config | 104 cfg svcconfig.Config |
91 // cfgHash is the hash string of the original config. | 105 // cfgHash is the hash string of the original config. |
92 cfgHash string | 106 cfgHash string |
93 | 107 |
| 108 // projectConfigCache is a cache of project-specific configs. They will |
| 109 // eventually timeout and get refreshed according to options. |
| 110 projectConfigCache proccache.Cache |
| 111 |
94 // configChangedC will contain the result of the most recent configurati
on | 112 // configChangedC will contain the result of the most recent configurati
on |
95 // change poll operation. The configuration change poller will block unt
il | 113 // change poll operation. The configuration change poller will block unt
il |
96 // that result is consumed. | 114 // that result is consumed. |
97 configChangedC chan struct{} | 115 configChangedC chan struct{} |
98 // changePollerCancelFunc is the cancel function to call to stop the | 116 // changePollerCancelFunc is the cancel function to call to stop the |
99 // configuration poller. | 117 // configuration poller. |
100 changePollerCancelFunc func() | 118 changePollerCancelFunc func() |
101 } | 119 } |
102 | 120 |
103 // NewManager generates a new Manager and loads the initial configuration. | 121 // NewManager generates a new Manager and loads the initial configuration. |
(...skipping 19 matching lines...) Expand all Loading... |
123 } | 141 } |
124 | 142 |
125 return &m, nil | 143 return &m, nil |
126 } | 144 } |
127 | 145 |
128 // Config returns the service configuration instance. | 146 // Config returns the service configuration instance. |
129 func (m *Manager) Config() *svcconfig.Config { | 147 func (m *Manager) Config() *svcconfig.Config { |
130 return &m.cfg | 148 return &m.cfg |
131 } | 149 } |
132 | 150 |
| 151 // ProjectConfig returns the project configuration. |
| 152 func (m *Manager) ProjectConfig(c context.Context, project config.ProjectName) (
*svcconfig.ProjectConfig, error) { |
| 153 serviceID := m.o.ServiceID |
| 154 if serviceID == "" { |
| 155 return nil, errors.New("no service ID specified") |
| 156 } |
| 157 |
| 158 v, err := proccache.GetOrMake(c, project, func() (interface{}, time.Dura
tion, error) { |
| 159 configSet := fmt.Sprintf("projects/%s", project) |
| 160 configPath := fmt.Sprintf("%s.cfg", serviceID) |
| 161 cfg, err := m.o.Config.GetConfig(configSet, configPath, false) |
| 162 if err != nil { |
| 163 log.Fields{ |
| 164 log.ErrorKey: err, |
| 165 "project": project, |
| 166 }.Errorf(c, "Failed to load config.") |
| 167 return nil, 0, err |
| 168 } |
| 169 |
| 170 var pcfg svcconfig.ProjectConfig |
| 171 if err := proto.UnmarshalText(cfg.Content, &pcfg); err != nil { |
| 172 log.Fields{ |
| 173 log.ErrorKey: err, |
| 174 "project": project, |
| 175 "configSet": configSet, |
| 176 "path": configPath, |
| 177 "hash": cfg.ContentHash, |
| 178 }.Errorf(c, "Failed to unmarshal project config.") |
| 179 } |
| 180 |
| 181 log.Fields{ |
| 182 "cacheDuration": m.o.ProjectConfigCacheDuration, |
| 183 "project": project, |
| 184 "configSet": configSet, |
| 185 "path": configPath, |
| 186 "hash": cfg.ContentHash, |
| 187 }.Infof(c, "Refreshed project configuration.") |
| 188 return &pcfg, m.o.ProjectConfigCacheDuration, nil |
| 189 }) |
| 190 if err != nil { |
| 191 return nil, err |
| 192 } |
| 193 return v.(*svcconfig.ProjectConfig), nil |
| 194 } |
| 195 |
133 // Close terminates the config change poller and blocks until it has finished. | 196 // Close terminates the config change poller and blocks until it has finished. |
134 // | 197 // |
135 // Close must be called in order to ensure that Go scheduler properly schedules | 198 // Close must be called in order to ensure that Go scheduler properly schedules |
136 // the goroutine. | 199 // the goroutine. |
137 func (m *Manager) Close() { | 200 func (m *Manager) Close() { |
138 // If our config change poller is running, cancel and reap it. | 201 // If our config change poller is running, cancel and reap it. |
139 if m.changePollerCancelFunc != nil { | 202 if m.changePollerCancelFunc != nil { |
140 m.changePollerCancelFunc() | 203 m.changePollerCancelFunc() |
141 <-m.configChangedC | 204 <-m.configChangedC |
142 } | 205 } |
143 } | 206 } |
144 | 207 |
145 func (m *Manager) reloadConfig(c context.Context) error { | 208 func (m *Manager) reloadConfig(c context.Context) error { |
146 cfg, err := m.o.getConfig(false) | 209 cfg, err := m.o.getConfig(false) |
147 if err != nil { | 210 if err != nil { |
148 return err | 211 return err |
149 } | 212 } |
150 | 213 |
151 if err := proto.UnmarshalText(cfg.Content, &m.cfg); err != nil { | 214 if err := proto.UnmarshalText(cfg.Content, &m.cfg); err != nil { |
152 log.Fields{ | 215 log.Fields{ |
153 log.ErrorKey: err, | 216 log.ErrorKey: err, |
154 "hash": cfg.ContentHash, | 217 "hash": cfg.ContentHash, |
155 }.Errorf(c, "Failed to unmarshal configuration.") | 218 }.Errorf(c, "Failed to unmarshal configuration.") |
156 return err | 219 return err |
157 } | 220 } |
158 m.cfgHash = cfg.ContentHash | 221 m.cfgHash = cfg.ContentHash |
159 return nil | 222 return nil |
160 } | 223 } |
OLD | NEW |