forked from robustirc/robustirc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
canary.go
122 lines (103 loc) · 3.01 KB
/
canary.go
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
113
114
115
116
117
118
119
120
121
122
package main
import (
"encoding/json"
"log"
"os"
"sort"
"strconv"
"strings"
"github.com/hashicorp/raft"
"github.com/robustirc/robustirc/types"
"github.com/robustirc/robustirc/util"
"github.com/sorcix/irc"
)
type uint64Slice []uint64
func (p uint64Slice) Len() int { return len(p) }
func (p uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type canaryMessageOutput struct {
Text string
InterestingFor map[string]bool
}
type canaryMessageState struct {
Id uint64
Session int64
Input string
Output []canaryMessageOutput
Compacted bool
}
func messagesString(messages []*types.RobustMessage) string {
output := make([]string, len(messages))
for idx, msg := range messages {
output[idx] = "→ " + msg.Data
}
return strings.Join(output, "\n")
}
func canary(fsm raft.FSM, statePath string) {
// Create a snapshot (only creates metadata) and persist it (does the
// actual compaction). Afterwards we have access to |rs.parsed| (all
// raft log entries, but parsed) and |rs.del| (all messages which were
// just compacted).
log.Printf("Compacting before dumping state\n")
snapshot, err := fsm.Snapshot()
if err != nil {
log.Fatalf("fsm.Snapshot(): %v\n", err)
}
rs, ok := snapshot.(*robustSnapshot)
if !ok {
log.Fatalf("snapshot is not a robustSnapshot")
}
sink, err := raft.NewDiscardSnapshotStore().Create(rs.lastIndex, 1, []byte{})
if err != nil {
log.Fatalf("DiscardSnapshotStore.Create(): %v\n", err)
}
if err := snapshot.Persist(sink); err != nil {
log.Fatalf("snapshot.Persist(): %v\n", err)
}
// Dump the in-memory state into a file, to be read by robustirc-canary.
f, err := os.Create(statePath)
if err != nil {
log.Fatal(err)
}
defer f.Close()
log.Printf("Dumping state for robustirc-canary into %q\n", statePath)
enc := json.NewEncoder(f)
// Sort the keys to iterate through |rs.parsed| in deterministic order.
keys := make([]uint64, 0, len(rs.parsed))
for idx := range rs.parsed {
keys = append(keys, idx)
}
sort.Sort(uint64Slice(keys))
for _, idx := range keys {
nmsg := rs.parsed[idx]
// TODO: come up with pseudo-values for createsession/deletesession
if nmsg.Type != types.RobustIRCFromClient {
continue
}
ircmsg := irc.ParseMessage(nmsg.Data)
if ircmsg.Command == irc.PING || ircmsg.Command == irc.PONG {
continue
}
vmsgs, _ := ircServer.Get(nmsg.Id)
cm := canaryMessageState{
Id: idx,
Session: nmsg.Session.Id,
Input: util.PrivacyFilterIrcmsg(ircmsg).String(),
Output: make([]canaryMessageOutput, len(vmsgs)),
Compacted: rs.del[idx],
}
for idx, vmsg := range vmsgs {
ifc := make(map[string]bool)
for k, v := range vmsg.InterestingFor {
ifc["0x"+strconv.FormatInt(k, 16)] = v
}
cm.Output[idx] = canaryMessageOutput{
Text: util.PrivacyFilterIrcmsg(irc.ParseMessage(vmsg.Data)).String(),
InterestingFor: ifc,
}
}
if err := enc.Encode(&cm); err != nil {
log.Fatal(err)
}
}
}