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

Side by Side Diff: logdog/server/service/config/config.go

Issue 2647083003: LogDog: Use luci_config/server packages for config (Closed)
Patch Set: Comments. 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 | « logdog/server/cmd/logdog_collector/main.go ('k') | logdog/server/service/config/config_test.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 config
6
7 import (
8 "errors"
9 "fmt"
10 "time"
11
12 "github.com/luci/luci-go/common/clock"
13 "github.com/luci/luci-go/common/config"
14 "github.com/luci/luci-go/common/data/caching/proccache"
15 log "github.com/luci/luci-go/common/logging"
16 "github.com/luci/luci-go/logdog/api/config/svcconfig"
17 "github.com/luci/luci-go/luci_config/common/cfgtypes"
18
19 "github.com/golang/protobuf/proto"
20 "golang.org/x/net/context"
21 )
22
23 // Options is the set of options used to set up a Manager.
24 //
25 // The configuration is loaded from a svcconfig.Config protobuf.
26 type Options struct {
27 // Config is the configuration service to load from.
28 Config config.Interface
29
30 // ServiceID is used to load project configurations, which are named aft er a
31 // specific service.
32 //
33 // If empty, project configurations cannot be loaded.
34 ServiceID string
35
36 // ConfigSet is the name of the ConfigSet to load.
37 ConfigSet string
38 // ServiceConfigPath is the name of the LogDog service config within the
39 // ConfigSet.
40 ServiceConfigPath string
41
42 // ProjectConfigCacheDuration is the amount of time to cache a project's
43 // configuration. If this is <= 0, the project config will be fetched ea ch
44 // time it's requested.
45 ProjectConfigCacheDuration time.Duration
46
47 // KillCheckInterval, if >0, starts a goroutine that polls every interva l to
48 // see if the configuration has changed. If it has, KillFunc will be inv oked.
49 KillCheckInterval time.Duration
50 // KillFunc is the function that will be called if a configuration hash change
51 // has been observed.
52 KillFunc func()
53 }
54
55 func (o *Options) getConfig(c context.Context, hashOnly bool) (*config.Config, e rror) {
56 return o.Config.GetConfig(c, o.ConfigSet, o.ServiceConfigPath, hashOnly)
57 }
58
59 func (o *Options) pollForConfigChanges(c context.Context, hash string) {
60 for {
61 log.Fields{
62 "timeout": o.KillCheckInterval,
63 }.Debugf(c, "Entering kill check poll loop...")
64
65 if tr := clock.Sleep(c, o.KillCheckInterval); tr.Incomplete() {
66 log.WithError(c.Err()).Debugf(c, "Context cancelled, shu tting down kill poller.")
67 return
68 }
69
70 log.Infof(c, "Kill check timeout triggered, checking configurati on...")
71 cfg, err := o.getConfig(c, true)
72 if err != nil {
73 log.WithError(err).Warningf(c, "Failed to reload configu ration.")
74 continue
75 }
76
77 if cfg.ContentHash != hash {
78 log.Fields{
79 "originalHash": hash,
80 "newHash": cfg.ContentHash,
81 }.Errorf(c, "Configuration content hash has changed.")
82 o.runKillFunc()
83 return
84 }
85
86 log.Fields{
87 "currentHash": cfg.ContentHash,
88 }.Debugf(c, "Content hash matches.")
89 }
90 }
91
92 func (o *Options) runKillFunc() {
93 if f := o.KillFunc; f != nil {
94 f()
95 }
96 }
97
98 // Manager holds and exposes a service configuration.
99 //
100 // It can also periodically refresh that configuration and invoke a shutdown
101 // function if its content changes.
102 type Manager struct {
103 o *Options
104
105 // cfg is the initial configuration.
106 cfg svcconfig.Config
107 // cfgHash is the hash string of the original config.
108 cfgHash string
109
110 // projectConfigCache is a cache of project-specific configs. They will
111 // eventually timeout and get refreshed according to options.
112 projectConfigCache proccache.Cache
113
114 // configChangedC will contain the result of the most recent configurati on
115 // change poll operation. The configuration change poller will block unt il
116 // that result is consumed.
117 configChangedC chan struct{}
118 // changePollerCancelFunc is the cancel function to call to stop the
119 // configuration poller.
120 changePollerCancelFunc func()
121 }
122
123 // NewManager generates a new Manager and loads the initial configuration.
124 func NewManager(c context.Context, o Options) (*Manager, error) {
125 m := Manager{
126 o: &o,
127 }
128
129 // Load the initial configuration.
130 if err := m.reloadConfig(c); err != nil {
131 return nil, err
132 }
133
134 if o.KillCheckInterval > 0 {
135 m.configChangedC = make(chan struct{})
136
137 var cancelC context.Context
138 cancelC, m.changePollerCancelFunc = context.WithCancel(c)
139 go func() {
140 defer close(m.configChangedC)
141 m.o.pollForConfigChanges(cancelC, m.cfgHash)
142 }()
143 }
144
145 return &m, nil
146 }
147
148 // Config returns the service configuration instance.
149 func (m *Manager) Config() *svcconfig.Config {
150 return &m.cfg
151 }
152
153 // ProjectConfig returns the project configuration.
154 func (m *Manager) ProjectConfig(c context.Context, project cfgtypes.ProjectName) (*svcconfig.ProjectConfig, error) {
155 serviceID := m.o.ServiceID
156 if serviceID == "" {
157 return nil, errors.New("no service ID specified")
158 }
159
160 v, err := proccache.GetOrMake(c, project, func() (interface{}, time.Dura tion, error) {
161 configSet := fmt.Sprintf("projects/%s", project)
162 configPath := fmt.Sprintf("%s.cfg", serviceID)
163 cfg, err := m.o.Config.GetConfig(c, configSet, configPath, false )
164 if err != nil {
165 log.Fields{
166 log.ErrorKey: err,
167 "project": project,
168 "configSet": configSet,
169 "path": configPath,
170 }.Errorf(c, "Failed to load config.")
171 return nil, 0, err
172 }
173
174 var pcfg svcconfig.ProjectConfig
175 if err := proto.UnmarshalText(cfg.Content, &pcfg); err != nil {
176 log.Fields{
177 log.ErrorKey: err,
178 "project": project,
179 "configSet": configSet,
180 "path": configPath,
181 "hash": cfg.ContentHash,
182 }.Errorf(c, "Failed to unmarshal project config.")
183 }
184
185 log.Fields{
186 "cacheDuration": m.o.ProjectConfigCacheDuration,
187 "project": project,
188 "configSet": configSet,
189 "path": configPath,
190 "hash": cfg.ContentHash,
191 }.Infof(c, "Refreshed project configuration.")
192 return &pcfg, m.o.ProjectConfigCacheDuration, nil
193 })
194 if err != nil {
195 return nil, err
196 }
197 return v.(*svcconfig.ProjectConfig), nil
198 }
199
200 // Close terminates the config change poller and blocks until it has finished.
201 //
202 // Close must be called in order to ensure that Go scheduler properly schedules
203 // the goroutine.
204 func (m *Manager) Close() {
205 // If our config change poller is running, cancel and reap it.
206 if m.changePollerCancelFunc != nil {
207 m.changePollerCancelFunc()
208 <-m.configChangedC
209 }
210 }
211
212 func (m *Manager) reloadConfig(c context.Context) error {
213 cfg, err := m.o.getConfig(c, false)
214 if err != nil {
215 return err
216 }
217
218 if err := proto.UnmarshalText(cfg.Content, &m.cfg); err != nil {
219 log.Fields{
220 log.ErrorKey: err,
221 "hash": cfg.ContentHash,
222 }.Errorf(c, "Failed to unmarshal configuration.")
223 return err
224 }
225 m.cfgHash = cfg.ContentHash
226 return nil
227 }
OLDNEW
« no previous file with comments | « logdog/server/cmd/logdog_collector/main.go ('k') | logdog/server/service/config/config_test.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698