| Index: logdog/server/service/config/poller.go
|
| diff --git a/logdog/server/service/config/poller.go b/logdog/server/service/config/poller.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d7abb80dc71088a8a03c9396fdf162f23f377206
|
| --- /dev/null
|
| +++ b/logdog/server/service/config/poller.go
|
| @@ -0,0 +1,103 @@
|
| +// Copyright 2016 The LUCI Authors. All rights reserved.
|
| +// Use of this source code is governed under the Apache License, Version 2.0
|
| +// that can be found in the LICENSE file.
|
| +
|
| +package config
|
| +
|
| +import (
|
| + "time"
|
| +
|
| + "github.com/luci/luci-go/common/clock"
|
| + "github.com/luci/luci-go/common/errors"
|
| + log "github.com/luci/luci-go/common/logging"
|
| + "github.com/luci/luci-go/luci_config/common/cfgtypes"
|
| + "github.com/luci/luci-go/luci_config/server/cfgclient"
|
| +
|
| + "golang.org/x/net/context"
|
| +)
|
| +
|
| +// ChangePoller polls a configuration files for changes. If it changes,
|
| +// the OnChange function will be called and the polling will stop.
|
| +type ChangePoller struct {
|
| + // ConfigSet is the slice of config paths to watch.
|
| + ConfigSet cfgtypes.ConfigSet
|
| + // Path is the path of the config to watch.
|
| + Path string
|
| +
|
| + // Period is the amount of time in between checks to see if the configuration
|
| + // has been updated. If <= 0, the poller will refrain from polling, and Run
|
| + // will immediately exit.
|
| + Period time.Duration
|
| + // OnChange is the function that will be called if a configuration change has
|
| + // been observed.
|
| + //
|
| + // Polling will be blocked until OnChange returns. If the Context supplied to
|
| + // Run is canceled by OnChange, Run will exit at the beginning of the next
|
| + // poll round.
|
| + OnChange func()
|
| +
|
| + // ContentHash is the config's hash. This should be set to the initial config
|
| + // value, either directly or via a Refresh call, before Run is called.
|
| + ContentHash string
|
| +}
|
| +
|
| +// Run starts polling for changes. It will stop when the Context is cancelled.
|
| +func (p *ChangePoller) Run(c context.Context) {
|
| + if p.Period <= 0 {
|
| + return
|
| + }
|
| +
|
| + for {
|
| + // If our Context has been canceled, terminate.
|
| + select {
|
| + case <-c.Done():
|
| + log.WithError(c.Err()).Warningf(c, "Terminating poll loop: context has been cancelled.")
|
| + return
|
| + default:
|
| + // Continue
|
| + }
|
| +
|
| + log.Fields{
|
| + "timeout": p.Period,
|
| + }.Debugf(c, "Entering change check poll loop...")
|
| + if tr := clock.Sleep(c, p.Period); tr.Incomplete() {
|
| + log.WithError(tr.Err).Debugf(c, "Context cancelled, shutting down change poller.")
|
| + return
|
| + }
|
| +
|
| + log.Infof(c, "Change check timeout triggered, checking configuration...")
|
| + lastHash := p.ContentHash
|
| + switch err := p.Refresh(c); {
|
| + case err != nil:
|
| + log.WithError(err).Errorf(c, "Failed to refresh config.")
|
| +
|
| + case lastHash != p.ContentHash:
|
| + log.Fields{
|
| + "originalHash": lastHash,
|
| + "newHash": p.ContentHash,
|
| + }.Warningf(c, "Configuration content hash has changed.")
|
| + if p.OnChange != nil {
|
| + p.OnChange()
|
| + }
|
| +
|
| + default:
|
| + log.Fields{
|
| + "currentHash": lastHash,
|
| + }.Debugf(c, "Content hash matches.")
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Refresh reloads the configuration value, updating ContentHash.
|
| +func (p *ChangePoller) Refresh(c context.Context) error {
|
| + var meta cfgclient.Meta
|
| + if err := cfgclient.Get(c, cfgclient.AsService, p.ConfigSet, p.Path, nil, &meta); err != nil {
|
| + return errors.Annotate(err).Reason("failed to reload config %(configSet)s :: %(path)s").
|
| + D("configSet", p.ConfigSet).
|
| + D("path", p.Path).
|
| + Err()
|
| + }
|
| +
|
| + p.ContentHash = meta.ContentHash
|
| + return nil
|
| +}
|
|
|