master
go 145 lines 2.83 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package jobmgr
4
5 import (
6 "encoding/json"
7 "fmt"
8 "os"
9 "path/filepath"
10 "slices"
11 "strings"
12 "sync"
13
14 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
15 "github.com/netdata/netdata/go/plugins/plugin/framework/filepersister"
16 )
17
18 func statusFileName(dir, pluginName string) string {
19 name := strings.ReplaceAll(pluginName, ".", "")
20 return filepath.Join(dir, fmt.Sprintf("%s-jobs-statuses.json", name))
21 }
22
23 func (m *Manager) loadFileStatus() {
24 m.fileStatus = newFileStatus()
25
26 if !m.runModePolicy.UseFileStatusPersistence || m.varLibDir == "" {
27 return
28 }
29
30 s, err := loadFileStatus(statusFileName(m.varLibDir, m.pluginName))
31 if err != nil {
32 m.Warningf("failed to load state file: %v", err)
33 return
34 }
35 m.fileStatus = s
36 }
37
38 func (m *Manager) runFileStatusPersistence() {
39 if !m.runModePolicy.UseFileStatusPersistence || m.varLibDir == "" {
40 return
41 }
42
43 p := filepersister.New(statusFileName(m.varLibDir, m.pluginName))
44
45 p.Run(m.ctx, m.fileStatus)
46 }
47
48 func loadFileStatus(path string) (*fileStatus, error) {
49 f, err := os.Open(path)
50 if err != nil {
51 return nil, err
52 }
53 defer func() { _ = f.Close() }()
54
55 s := newFileStatus()
56
57 return s, json.NewDecoder(f).Decode(&s.items)
58 }
59
60 func newFileStatus() *fileStatus {
61 return &fileStatus{
62 items: make(map[string]map[string]string),
63 ch: make(chan struct{}, 1),
64 }
65 }
66
67 type fileStatus struct {
68 mux sync.Mutex
69 items map[string]map[string]string // [module][name:hash]status
70 ch chan struct{}
71 }
72
73 func (s *fileStatus) Bytes() ([]byte, error) {
74 s.mux.Lock()
75 defer s.mux.Unlock()
76
77 return json.MarshalIndent(s.items, "", " ")
78 }
79
80 func (s *fileStatus) Updated() <-chan struct{} {
81 return s.ch
82 }
83
84 func (s *fileStatus) contains(cfg confgroup.Config, statuses ...string) bool {
85 status, ok := s.lookup(cfg)
86 if !ok {
87 return false
88 }
89
90 return slices.Contains(statuses, status)
91 }
92
93 func (s *fileStatus) lookup(cfg confgroup.Config) (string, bool) {
94 s.mux.Lock()
95 defer s.mux.Unlock()
96
97 jobs, ok := s.items[cfg.Module()]
98 if !ok {
99 return "", false
100 }
101
102 status, ok := jobs[s.jobKey(cfg)]
103
104 return status, ok
105 }
106
107 func (s *fileStatus) add(cfg confgroup.Config, status string) {
108 s.mux.Lock()
109 defer s.mux.Unlock()
110
111 defer s.setUpdated()
112
113 if s.items == nil {
114 s.items = make(map[string]map[string]string)
115 }
116
117 if s.items[cfg.Module()] == nil {
118 s.items[cfg.Module()] = make(map[string]string)
119 }
120
121 s.items[cfg.Module()][s.jobKey(cfg)] = status
122 }
123
124 func (s *fileStatus) remove(cfg confgroup.Config) {
125 s.mux.Lock()
126 defer s.mux.Unlock()
127
128 defer s.setUpdated()
129
130 delete(s.items[cfg.Module()], s.jobKey(cfg))
131
132 if len(s.items[cfg.Module()]) == 0 {
133 delete(s.items, cfg.Module())
134 }
135 }
136 func (s *fileStatus) setUpdated() {
137 select {
138 case s.ch <- struct{}{}:
139 default:
140 }
141 }
142
143 func (s *fileStatus) jobKey(cfg confgroup.Config) string {
144 return fmt.Sprintf("%s:%d", cfg.Name(), cfg.Hash())
145 }