status_file.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package health
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "path"
  8. "strings"
  9. "sync"
  10. "time"
  11. )
  12. type StatusFile struct {
  13. filename string
  14. mu sync.RWMutex
  15. m StatusFileData
  16. }
  17. type StatusFileData map[string]*Service
  18. func NewStatusFile(filename string) *StatusFile {
  19. return &StatusFile{
  20. m: make(StatusFileData),
  21. filename: filename,
  22. }
  23. }
  24. // DirectoryReader loads (and regularly re-loads) health
  25. // .json files from the specified files into the default
  26. // health registry.
  27. func DirectoryReader(dir string) {
  28. for {
  29. err := reloadDirectory(dir)
  30. if err != nil {
  31. log.Printf("loading health data: %s", err)
  32. }
  33. time.Sleep(1 * time.Second)
  34. }
  35. }
  36. func reloadDirectory(dir string) error {
  37. dirlist, err := ioutil.ReadDir(dir)
  38. if err != nil {
  39. return fmt.Errorf("could not read '%s': %s", dir, err)
  40. }
  41. seen := map[string]bool{}
  42. var parseErr error
  43. for _, file := range dirlist {
  44. fileName := file.Name()
  45. if !strings.HasSuffix(strings.ToLower(fileName), ".json") ||
  46. strings.HasPrefix(path.Base(fileName), ".") ||
  47. file.IsDir() {
  48. continue
  49. }
  50. statusName := fileName[0:strings.LastIndex(fileName, ".")]
  51. registry.mu.Lock()
  52. s, ok := registry.m[statusName]
  53. registry.mu.Unlock()
  54. seen[statusName] = true
  55. if ok {
  56. s.Reload()
  57. } else {
  58. s := NewStatusFile(path.Join(dir, fileName))
  59. err := s.Reload()
  60. if err != nil {
  61. log.Printf("error loading '%s': %s", fileName, err)
  62. parseErr = err
  63. }
  64. registry.Add(statusName, s)
  65. }
  66. }
  67. registry.mu.Lock()
  68. for n, _ := range registry.m {
  69. if !seen[n] {
  70. registry.m[n].Close()
  71. delete(registry.m, n)
  72. }
  73. }
  74. registry.mu.Unlock()
  75. return parseErr
  76. }
  77. func (s *StatusFile) Reload() error {
  78. if len(s.filename) > 0 {
  79. return s.Load(s.filename)
  80. }
  81. return nil
  82. }
  83. // Load imports the data atomically into the status map. If there's
  84. // a JSON error the old data is preserved.
  85. func (s *StatusFile) Load(filename string) error {
  86. n := StatusFileData{}
  87. b, err := ioutil.ReadFile(filename)
  88. if err != nil {
  89. return err
  90. }
  91. err = json.Unmarshal(b, &n)
  92. if err != nil {
  93. return err
  94. }
  95. s.mu.Lock()
  96. s.m = n
  97. s.mu.Unlock()
  98. return nil
  99. }
  100. func (s *StatusFile) Close() error {
  101. s.mu.Lock()
  102. s.m = nil
  103. s.mu.Unlock()
  104. return nil
  105. }
  106. func (s *StatusFile) GetStatus(check string) StatusType {
  107. s.mu.RLock()
  108. defer s.mu.RUnlock()
  109. if s.m == nil {
  110. return StatusUnknown
  111. }
  112. st, ok := s.m[check]
  113. if !ok {
  114. log.Printf("Not found '%s'", check)
  115. return StatusUnknown
  116. }
  117. return st.Status
  118. }
  119. // UnmarshalJSON implements the json.Unmarshaler interface.
  120. func (srv *Service) UnmarshalJSON(b []byte) error {
  121. var i int64
  122. if err := json.Unmarshal(b, &i); err != nil {
  123. return err
  124. }
  125. *srv = Service{Status: StatusType(i)}
  126. return nil
  127. }
  128. // UnmarshalJSON implements the json.Marshaler interface.
  129. // func (srv *Service) MarshalJSON() ([]byte, error) {
  130. // return
  131. // }