| Index: logdog/server/service/config/config.go
|
| diff --git a/logdog/server/service/config/config.go b/logdog/server/service/config/config.go
|
| deleted file mode 100644
|
| index e9293a3bd50270397bcfe7f00812917d6acec288..0000000000000000000000000000000000000000
|
| --- a/logdog/server/service/config/config.go
|
| +++ /dev/null
|
| @@ -1,227 +0,0 @@
|
| -// 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 (
|
| - "errors"
|
| - "fmt"
|
| - "time"
|
| -
|
| - "github.com/luci/luci-go/common/clock"
|
| - "github.com/luci/luci-go/common/config"
|
| - "github.com/luci/luci-go/common/data/caching/proccache"
|
| - log "github.com/luci/luci-go/common/logging"
|
| - "github.com/luci/luci-go/logdog/api/config/svcconfig"
|
| - "github.com/luci/luci-go/luci_config/common/cfgtypes"
|
| -
|
| - "github.com/golang/protobuf/proto"
|
| - "golang.org/x/net/context"
|
| -)
|
| -
|
| -// Options is the set of options used to set up a Manager.
|
| -//
|
| -// The configuration is loaded from a svcconfig.Config protobuf.
|
| -type Options struct {
|
| - // Config is the configuration service to load from.
|
| - Config config.Interface
|
| -
|
| - // ServiceID is used to load project configurations, which are named after a
|
| - // specific service.
|
| - //
|
| - // If empty, project configurations cannot be loaded.
|
| - ServiceID string
|
| -
|
| - // ConfigSet is the name of the ConfigSet to load.
|
| - ConfigSet string
|
| - // ServiceConfigPath is the name of the LogDog service config within the
|
| - // ConfigSet.
|
| - ServiceConfigPath string
|
| -
|
| - // ProjectConfigCacheDuration is the amount of time to cache a project's
|
| - // configuration. If this is <= 0, the project config will be fetched each
|
| - // time it's requested.
|
| - ProjectConfigCacheDuration time.Duration
|
| -
|
| - // KillCheckInterval, if >0, starts a goroutine that polls every interval to
|
| - // see if the configuration has changed. If it has, KillFunc will be invoked.
|
| - KillCheckInterval time.Duration
|
| - // KillFunc is the function that will be called if a configuration hash change
|
| - // has been observed.
|
| - KillFunc func()
|
| -}
|
| -
|
| -func (o *Options) getConfig(c context.Context, hashOnly bool) (*config.Config, error) {
|
| - return o.Config.GetConfig(c, o.ConfigSet, o.ServiceConfigPath, hashOnly)
|
| -}
|
| -
|
| -func (o *Options) pollForConfigChanges(c context.Context, hash string) {
|
| - for {
|
| - log.Fields{
|
| - "timeout": o.KillCheckInterval,
|
| - }.Debugf(c, "Entering kill check poll loop...")
|
| -
|
| - if tr := clock.Sleep(c, o.KillCheckInterval); tr.Incomplete() {
|
| - log.WithError(c.Err()).Debugf(c, "Context cancelled, shutting down kill poller.")
|
| - return
|
| - }
|
| -
|
| - log.Infof(c, "Kill check timeout triggered, checking configuration...")
|
| - cfg, err := o.getConfig(c, true)
|
| - if err != nil {
|
| - log.WithError(err).Warningf(c, "Failed to reload configuration.")
|
| - continue
|
| - }
|
| -
|
| - if cfg.ContentHash != hash {
|
| - log.Fields{
|
| - "originalHash": hash,
|
| - "newHash": cfg.ContentHash,
|
| - }.Errorf(c, "Configuration content hash has changed.")
|
| - o.runKillFunc()
|
| - return
|
| - }
|
| -
|
| - log.Fields{
|
| - "currentHash": cfg.ContentHash,
|
| - }.Debugf(c, "Content hash matches.")
|
| - }
|
| -}
|
| -
|
| -func (o *Options) runKillFunc() {
|
| - if f := o.KillFunc; f != nil {
|
| - f()
|
| - }
|
| -}
|
| -
|
| -// Manager holds and exposes a service configuration.
|
| -//
|
| -// It can also periodically refresh that configuration and invoke a shutdown
|
| -// function if its content changes.
|
| -type Manager struct {
|
| - o *Options
|
| -
|
| - // cfg is the initial configuration.
|
| - cfg svcconfig.Config
|
| - // cfgHash is the hash string of the original config.
|
| - cfgHash string
|
| -
|
| - // projectConfigCache is a cache of project-specific configs. They will
|
| - // eventually timeout and get refreshed according to options.
|
| - projectConfigCache proccache.Cache
|
| -
|
| - // configChangedC will contain the result of the most recent configuration
|
| - // change poll operation. The configuration change poller will block until
|
| - // that result is consumed.
|
| - configChangedC chan struct{}
|
| - // changePollerCancelFunc is the cancel function to call to stop the
|
| - // configuration poller.
|
| - changePollerCancelFunc func()
|
| -}
|
| -
|
| -// NewManager generates a new Manager and loads the initial configuration.
|
| -func NewManager(c context.Context, o Options) (*Manager, error) {
|
| - m := Manager{
|
| - o: &o,
|
| - }
|
| -
|
| - // Load the initial configuration.
|
| - if err := m.reloadConfig(c); err != nil {
|
| - return nil, err
|
| - }
|
| -
|
| - if o.KillCheckInterval > 0 {
|
| - m.configChangedC = make(chan struct{})
|
| -
|
| - var cancelC context.Context
|
| - cancelC, m.changePollerCancelFunc = context.WithCancel(c)
|
| - go func() {
|
| - defer close(m.configChangedC)
|
| - m.o.pollForConfigChanges(cancelC, m.cfgHash)
|
| - }()
|
| - }
|
| -
|
| - return &m, nil
|
| -}
|
| -
|
| -// Config returns the service configuration instance.
|
| -func (m *Manager) Config() *svcconfig.Config {
|
| - return &m.cfg
|
| -}
|
| -
|
| -// ProjectConfig returns the project configuration.
|
| -func (m *Manager) ProjectConfig(c context.Context, project cfgtypes.ProjectName) (*svcconfig.ProjectConfig, error) {
|
| - serviceID := m.o.ServiceID
|
| - if serviceID == "" {
|
| - return nil, errors.New("no service ID specified")
|
| - }
|
| -
|
| - v, err := proccache.GetOrMake(c, project, func() (interface{}, time.Duration, error) {
|
| - configSet := fmt.Sprintf("projects/%s", project)
|
| - configPath := fmt.Sprintf("%s.cfg", serviceID)
|
| - cfg, err := m.o.Config.GetConfig(c, configSet, configPath, false)
|
| - if err != nil {
|
| - log.Fields{
|
| - log.ErrorKey: err,
|
| - "project": project,
|
| - "configSet": configSet,
|
| - "path": configPath,
|
| - }.Errorf(c, "Failed to load config.")
|
| - return nil, 0, err
|
| - }
|
| -
|
| - var pcfg svcconfig.ProjectConfig
|
| - if err := proto.UnmarshalText(cfg.Content, &pcfg); err != nil {
|
| - log.Fields{
|
| - log.ErrorKey: err,
|
| - "project": project,
|
| - "configSet": configSet,
|
| - "path": configPath,
|
| - "hash": cfg.ContentHash,
|
| - }.Errorf(c, "Failed to unmarshal project config.")
|
| - }
|
| -
|
| - log.Fields{
|
| - "cacheDuration": m.o.ProjectConfigCacheDuration,
|
| - "project": project,
|
| - "configSet": configSet,
|
| - "path": configPath,
|
| - "hash": cfg.ContentHash,
|
| - }.Infof(c, "Refreshed project configuration.")
|
| - return &pcfg, m.o.ProjectConfigCacheDuration, nil
|
| - })
|
| - if err != nil {
|
| - return nil, err
|
| - }
|
| - return v.(*svcconfig.ProjectConfig), nil
|
| -}
|
| -
|
| -// Close terminates the config change poller and blocks until it has finished.
|
| -//
|
| -// Close must be called in order to ensure that Go scheduler properly schedules
|
| -// the goroutine.
|
| -func (m *Manager) Close() {
|
| - // If our config change poller is running, cancel and reap it.
|
| - if m.changePollerCancelFunc != nil {
|
| - m.changePollerCancelFunc()
|
| - <-m.configChangedC
|
| - }
|
| -}
|
| -
|
| -func (m *Manager) reloadConfig(c context.Context) error {
|
| - cfg, err := m.o.getConfig(c, false)
|
| - if err != nil {
|
| - return err
|
| - }
|
| -
|
| - if err := proto.UnmarshalText(cfg.Content, &m.cfg); err != nil {
|
| - log.Fields{
|
| - log.ErrorKey: err,
|
| - "hash": cfg.ContentHash,
|
| - }.Errorf(c, "Failed to unmarshal configuration.")
|
| - return err
|
| - }
|
| - m.cfgHash = cfg.ContentHash
|
| - return nil
|
| -}
|
|
|