-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtombstone.go
More file actions
112 lines (92 loc) · 2.49 KB
/
tombstone.go
File metadata and controls
112 lines (92 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package goplum
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"time"
)
var tombStonePath = flag.String("tombstone", "/tmp/goplum.tomb", "Path to save tombstones to persist data across restarts")
const maxTombStoneAge = 10 * time.Minute
type TombStone struct {
Time time.Time
Checks map[string]CheckTombStone
}
type CheckTombStone struct {
LastRun time.Time
Settled bool
State CheckState
Suspended bool
LastAlertTime time.Time `json:"last_alert_time,omitzero"`
History ResultHistory
PluginState json.RawMessage `json:"plugin_state,omitempty"`
}
func NewTombStone(checks map[string]*ScheduledCheck) *TombStone {
ts := &TombStone{
Time: time.Now(),
Checks: make(map[string]CheckTombStone),
}
for i := range checks {
var state []byte
check := checks[i]
if stateful, ok := check.Check.(Stateful); ok {
var err error
state, err = json.Marshal(stateful.Save())
if err != nil {
log.Printf("Unable to save state of check %s: %v", check.Name, err)
}
}
ts.Checks[check.Name] = CheckTombStone{
LastRun: check.LastRun,
Settled: check.Settled,
State: check.State,
Suspended: check.Suspended,
LastAlertTime: check.LastAlertTime,
History: check.History,
PluginState: state,
}
}
return ts
}
func LoadTombStone() (*TombStone, error) {
f, err := os.Open(*tombStonePath)
if err != nil {
return nil, err
}
defer f.Close()
tombStone := &TombStone{}
return tombStone, json.NewDecoder(f).Decode(tombStone)
}
func (ts *TombStone) Save() error {
f, err := os.OpenFile(*tombStonePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0600))
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(ts)
}
func (ts *TombStone) Restore(checks map[string]*ScheduledCheck) error {
if time.Since(ts.Time) >= maxTombStoneAge {
return fmt.Errorf("tombstone too old: %s", ts.Time)
}
for i := range checks {
check := checks[i]
if saved, ok := ts.Checks[check.Name]; ok {
check.LastRun = saved.LastRun
check.Settled = saved.Settled
check.State = saved.State
check.Suspended = saved.Suspended
check.LastAlertTime = saved.LastAlertTime
check.History = saved.History
if stateful, ok := check.Check.(Stateful); ok && saved.PluginState != nil {
stateful.Restore(func(i any) {
if err := json.Unmarshal(saved.PluginState, i); err != nil {
log.Printf("Unable to restore state of check %s: %v", check.Name, err)
}
})
}
}
}
return nil
}