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 config |
| 6 |
| 7 import ( |
| 8 "time" |
| 9 |
| 10 "github.com/luci/luci-go/common/clock" |
| 11 "github.com/luci/luci-go/common/errors" |
| 12 log "github.com/luci/luci-go/common/logging" |
| 13 "github.com/luci/luci-go/luci_config/common/cfgtypes" |
| 14 "github.com/luci/luci-go/luci_config/server/cfgclient" |
| 15 |
| 16 "golang.org/x/net/context" |
| 17 ) |
| 18 |
| 19 // ChangePoller polls a configuration files for changes. If it changes, |
| 20 // the OnChange function will be called and the polling will stop. |
| 21 type ChangePoller struct { |
| 22 // ConfigSet is the slice of config paths to watch. |
| 23 ConfigSet cfgtypes.ConfigSet |
| 24 // Path is the path of the config to watch. |
| 25 Path string |
| 26 |
| 27 // Period is the amount of time in between checks to see if the configur
ation |
| 28 // has been updated. If <= 0, the poller will refrain from polling, and
Run |
| 29 // will immediately exit. |
| 30 Period time.Duration |
| 31 // OnChange is the function that will be called if a configuration chang
e has |
| 32 // been observed. |
| 33 // |
| 34 // Polling will be blocked until OnChange returns. If the Context suppli
ed to |
| 35 // Run is canceled by OnChange, Run will exit at the beginning of the ne
xt |
| 36 // poll round. |
| 37 OnChange func() |
| 38 |
| 39 // ContentHash is the config's hash. This should be set to the initial c
onfig |
| 40 // value, either directly or via a Refresh call, before Run is called. |
| 41 ContentHash string |
| 42 } |
| 43 |
| 44 // Run starts polling for changes. It will stop when the Context is cancelled. |
| 45 func (p *ChangePoller) Run(c context.Context) { |
| 46 if p.Period <= 0 { |
| 47 return |
| 48 } |
| 49 |
| 50 for { |
| 51 // If our Context has been canceled, terminate. |
| 52 select { |
| 53 case <-c.Done(): |
| 54 log.WithError(c.Err()).Warningf(c, "Terminating poll loo
p: context has been cancelled.") |
| 55 return |
| 56 default: |
| 57 // Continue |
| 58 } |
| 59 |
| 60 log.Fields{ |
| 61 "timeout": p.Period, |
| 62 }.Debugf(c, "Entering change check poll loop...") |
| 63 if tr := clock.Sleep(c, p.Period); tr.Incomplete() { |
| 64 log.WithError(tr.Err).Debugf(c, "Context cancelled, shut
ting down change poller.") |
| 65 return |
| 66 } |
| 67 |
| 68 log.Infof(c, "Change check timeout triggered, checking configura
tion...") |
| 69 lastHash := p.ContentHash |
| 70 switch err := p.Refresh(c); { |
| 71 case err != nil: |
| 72 log.WithError(err).Errorf(c, "Failed to refresh config."
) |
| 73 |
| 74 case lastHash != p.ContentHash: |
| 75 log.Fields{ |
| 76 "originalHash": lastHash, |
| 77 "newHash": p.ContentHash, |
| 78 }.Warningf(c, "Configuration content hash has changed.") |
| 79 if p.OnChange != nil { |
| 80 p.OnChange() |
| 81 } |
| 82 |
| 83 default: |
| 84 log.Fields{ |
| 85 "currentHash": lastHash, |
| 86 }.Debugf(c, "Content hash matches.") |
| 87 } |
| 88 } |
| 89 } |
| 90 |
| 91 // Refresh reloads the configuration value, updating ContentHash. |
| 92 func (p *ChangePoller) Refresh(c context.Context) error { |
| 93 var meta cfgclient.Meta |
| 94 if err := cfgclient.Get(c, cfgclient.AsService, p.ConfigSet, p.Path, nil
, &meta); err != nil { |
| 95 return errors.Annotate(err).Reason("failed to reload config %(co
nfigSet)s :: %(path)s"). |
| 96 D("configSet", p.ConfigSet). |
| 97 D("path", p.Path). |
| 98 Err() |
| 99 } |
| 100 |
| 101 p.ContentHash = meta.ContentHash |
| 102 return nil |
| 103 } |
OLD | NEW |