manager.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package config
  2. import (
  3. "io"
  4. "os"
  5. "github.com/pkg/errors"
  6. "github.com/rs/zerolog"
  7. yaml "gopkg.in/yaml.v3"
  8. "github.com/cloudflare/cloudflared/watcher"
  9. )
  10. // Notifier sends out config updates
  11. type Notifier interface {
  12. ConfigDidUpdate(Root)
  13. }
  14. // Manager is the base functions of the config manager
  15. type Manager interface {
  16. Start(Notifier) error
  17. Shutdown()
  18. }
  19. // FileManager watches the yaml config for changes
  20. // sends updates to the service to reconfigure to match the updated config
  21. type FileManager struct {
  22. watcher watcher.Notifier
  23. notifier Notifier
  24. configPath string
  25. log *zerolog.Logger
  26. ReadConfig func(string, *zerolog.Logger) (Root, error)
  27. }
  28. // NewFileManager creates a config manager
  29. func NewFileManager(watcher watcher.Notifier, configPath string, log *zerolog.Logger) (*FileManager, error) {
  30. m := &FileManager{
  31. watcher: watcher,
  32. configPath: configPath,
  33. log: log,
  34. ReadConfig: readConfigFromPath,
  35. }
  36. err := watcher.Add(configPath)
  37. return m, err
  38. }
  39. // Start starts the runloop to watch for config changes
  40. func (m *FileManager) Start(notifier Notifier) error {
  41. m.notifier = notifier
  42. // update the notifier with a fresh config on start
  43. config, err := m.GetConfig()
  44. if err != nil {
  45. return err
  46. }
  47. notifier.ConfigDidUpdate(config)
  48. m.watcher.Start(m)
  49. return nil
  50. }
  51. // GetConfig reads the yaml file from the disk
  52. func (m *FileManager) GetConfig() (Root, error) {
  53. return m.ReadConfig(m.configPath, m.log)
  54. }
  55. // Shutdown stops the watcher
  56. func (m *FileManager) Shutdown() {
  57. m.watcher.Shutdown()
  58. }
  59. func readConfigFromPath(configPath string, log *zerolog.Logger) (Root, error) {
  60. if configPath == "" {
  61. return Root{}, errors.New("unable to find config file")
  62. }
  63. file, err := os.Open(configPath)
  64. if err != nil {
  65. return Root{}, err
  66. }
  67. defer file.Close()
  68. var config Root
  69. if err := yaml.NewDecoder(file).Decode(&config); err != nil {
  70. if err == io.EOF {
  71. log.Error().Msgf("Configuration file %s was empty", configPath)
  72. return Root{}, nil
  73. }
  74. return Root{}, errors.Wrap(err, "error parsing YAML in config file at "+configPath)
  75. }
  76. return config, nil
  77. }
  78. // File change notifications from the watcher
  79. // WatcherItemDidChange triggers when the yaml config is updated
  80. // sends the updated config to the service to reload its state
  81. func (m *FileManager) WatcherItemDidChange(filepath string) {
  82. config, err := m.GetConfig()
  83. if err != nil {
  84. m.log.Err(err).Msg("Failed to read new config")
  85. return
  86. }
  87. m.log.Info().Msg("Config file has been updated")
  88. m.notifier.ConfigDidUpdate(config)
  89. }
  90. // WatcherDidError notifies of errors with the file watcher
  91. func (m *FileManager) WatcherDidError(err error) {
  92. m.log.Err(err).Msg("Config watcher encountered an error")
  93. }