/
export.go
138 lines (111 loc) · 3.12 KB
/
export.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package meshservice
import (
"encoding/json"
ioutil "io/ioutil"
"regexp"
"strconv"
"strings"
"time"
"github.com/hashicorp/serf/serf"
log "github.com/sirupsen/logrus"
)
// SetMemberlistExportFile sets the file name for an export
// of the current memberlist. If empty no file is written
func (ms *MeshService) SetMemberlistExportFile(f string) {
ms.memberExportFile = f
}
type exportedMember struct {
Addr string `json:"addr"`
Status string `json:"st"`
RTT int64 `json:"rtt"`
Tags map[string]string `json:"tags"`
}
type exportedService struct {
Nodes []string `json:"nodes"`
Port int `json:"port"`
Tags map[string]string `json:"tags"`
}
type exportedMemberList struct {
Members map[string]exportedMember `json:"members"`
Services map[string]exportedService `json:"services"`
LastUpdate int64 `json:"lastUpdate"`
}
func (ms *MeshService) updateMemberExport() {
if ms.lastUpdatedTS.Unix() <= ms.lastExportedTS.Unix() {
return
}
log.Debug("updateMemberExport")
e := &exportedMemberList{
Members: make(map[string]exportedMember),
Services: make(map[string]exportedService),
LastUpdate: ms.lastUpdatedTS.Unix(),
}
myCoord, err := ms.Serf().GetCoordinate()
if err != nil {
log.WithError(err).Warn("Unable to get my own coordinate, check config")
myCoord = nil
}
for _, member := range ms.Serf().Members() {
em := exportedMember{
Addr: member.Addr.String(),
Status: member.Status.String(),
Tags: member.Tags,
}
// compute RTT if we have all distances
memberCoord, ok := ms.Serf().GetCachedCoordinate(member.Name)
if ok && memberCoord != nil {
d := memberCoord.DistanceTo(myCoord)
em.RTT = int64(d / time.Millisecond)
// TODO: for LAN mode add Microseconds as well
}
//
e.Members[member.Name] = em
// grab tags for service entries, put into service map
ms.processTagsForMember(&member, e)
}
content, err := json.MarshalIndent(e, "", " ")
if err != nil {
log.WithError(err).Error("unable to write to file")
}
ioutil.WriteFile(ms.memberExportFile, content, 0640)
ms.lastExportedTS = ms.lastUpdatedTS
}
func (ms *MeshService) processTagsForMember(member *serf.Member, e *exportedMemberList) {
svcKeyRe := regexp.MustCompile(`^svc:`)
for k, v := range member.Tags {
arr := svcKeyRe.Split(k, 2)
if arr != nil && len(arr) == 2 && arr[1] != "" {
expSvc, ex := e.Services[arr[1]]
if !ex {
expSvc = exportedService{
Tags: make(map[string]string),
Nodes: make([]string, 0),
}
}
// put member on the node list
expSvc.Nodes = append(expSvc.Nodes, member.Name)
// Split value
arrV := strings.Split(v, ",")
for _, elemV := range arrV {
arrE := strings.Split(elemV, "=")
if len(arrE) > 0 {
ek := arrE[0]
if ek == "port" && len(arrE) == 2 {
expSvc.Port, _ = strconv.Atoi(arrE[1])
continue
}
// put into general tags otherwise
if len(arrE) >= 1 {
ek = arrE[0]
}
ev := ""
if len(arrE) == 2 {
ev = arrE[1]
}
expSvc.Tags[ek] = ev
}
}
e.Services[arr[1]] = expSvc
}
}
}