Initial commit
[vaultmon/.git] / etcd.go
1 package main
2
3 import (
4         "encoding/json"
5         "fmt"
6         "io/ioutil"
7         "strconv"
8
9         log "github.com/sirupsen/logrus"
10 )
11
12 type Etcd struct {
13         URL              string
14         ConnectionStatus string
15         ClusterStatus    string
16 }
17
18 type EtcdHealthResult struct {
19         Health string `json:"health"`
20 }
21
22 type EtcdMembersEntry struct {
23         ClientURLs []string `json:"clientURLs"`
24 }
25
26 type EtcdMembersResult struct {
27         Members []EtcdMembersEntry `json:"members"`
28 }
29
30 func (e *Etcd) CheckHealth() (bool, error) {
31         healthURL := e.URL + "/health"
32         cnx := NewConnection(healthURL)
33         log.Debugf("Etcd connection on %s", healthURL)
34         resp, err := cnx.Get()
35         if err != nil {
36                 return false, err
37         }
38         defer resp.Body.Close()
39
40         log.Debugf("Etcd CheckHealth response: %s", resp.Status)
41
42         buf, err := ioutil.ReadAll(resp.Body)
43         if err != nil {
44                 return false, err
45         }
46
47         var result EtcdHealthResult
48
49         err = json.Unmarshal(buf, &result)
50         if err != nil {
51                 return false, err
52         }
53
54         log.Debugf("Etcd CheckHealth JSON result: %+v", result)
55
56         boolResult, err := strconv.ParseBool(result.Health)
57         if err != nil {
58                 return false, err
59         }
60
61         return boolResult == true, nil
62 }
63
64 func (e *Etcd) CheckCluster() (bool, error) {
65         clusterURL := e.URL + "/v2/members"
66         cnx := NewConnection(clusterURL)
67         log.Debugf("Etcd connection on %s", clusterURL)
68         resp, err := cnx.Get()
69         if err != nil {
70                 return false, err
71         }
72         defer resp.Body.Close()
73
74         log.Debugf("Etcd CheckCluster response: %s", resp.Status)
75
76         buf, err := ioutil.ReadAll(resp.Body)
77         if err != nil {
78                 return false, err
79         }
80
81         var result EtcdMembersResult
82
83         err = json.Unmarshal(buf, &result)
84         if err != nil {
85                 return false, err
86         }
87
88         alives := make([]bool, 0, len(result.Members))
89         for _, member := range result.Members {
90                 for _, client := range member.ClientURLs {
91                         alives = append(alives, getMemberHealth(client))
92                 }
93         }
94
95         log.Debugf("Etcd CheckCluster JSON result: %+v", result)
96
97         log.Debugf("Etcd Cluster Members alive result: %+v", alives)
98
99         return ensureMembersAlive(alives), nil
100 }
101
102 func getMemberHealth(memberUrl string) bool {
103         url := memberUrl + "/health"
104         cnx := NewConnection(url)
105         log.Debugf("Etcd Member connection on %s", url)
106         resp, err := cnx.Get()
107         if err != nil {
108                 log.Fatalf("Error Get: %s", err)
109         }
110         defer resp.Body.Close()
111
112         buf, err := ioutil.ReadAll(resp.Body)
113         if err != nil {
114                 log.Fatalf("Error ReadAll: %s", err)
115         }
116
117         var result EtcdHealthResult
118
119         err = json.Unmarshal(buf, &result)
120         if err != nil {
121                 log.Fatalf("Error Unmarshal: %s", err)
122         }
123
124         boolResult, err := strconv.ParseBool(result.Health)
125         if err != nil {
126                 log.Fatalf("Error ParseBool: %s", err)
127         }
128
129         return boolResult
130 }
131
132 func ensureMembersAlive(alives []bool) bool {
133         for _, alive := range alives {
134                 if alive != true {
135                         return false
136                 }
137         }
138
139         return true
140 }
141
142 func (e *Etcd) Test() (bool, string) {
143         // test health
144         result, err := e.CheckHealth()
145         if err != nil {
146                 log.Errorf("CheckHealth error: %s", err)
147                 e.ConnectionStatus = "KO"
148         }
149         e.ConnectionStatus = "OK"
150         log.Debugf("Etcd CheckHealth result: %t", result)
151
152         // test cluster
153         result, err = e.CheckCluster()
154         if err != nil {
155                 log.Errorf("CheckCluster error: %s", err)
156                 e.ClusterStatus = "KO"
157         }
158         e.ClusterStatus = "OK"
159         log.Debugf("Etcd CheckCluster result: %t", result)
160
161         return result, fmt.Sprintf("> %s `%s` status *%s*. Cluster status *%s*.", e.Name(), e.URL, e.ConnectionStatus, e.ClusterStatus)
162 }
163
164 func (e *Etcd) Name() string {
165         return "Etcd"
166 }
167
168 func NewEtcd(url string) CheckProvider {
169         return &Etcd{URL: url}
170 }