12260: Improve data structures.
[arvados.git] / sdk / go / health / aggregator_test.go
1 package health
2
3 import (
4         "encoding/json"
5         "net/http"
6         "net/http/httptest"
7         "strings"
8         "time"
9
10         "git.curoverse.com/arvados.git/sdk/go/arvados"
11         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
12         "gopkg.in/check.v1"
13 )
14
15 type AggregatorSuite struct {
16         handler *Aggregator
17         req     *http.Request
18         resp    *httptest.ResponseRecorder
19 }
20
21 // Gocheck boilerplate
22 var _ = check.Suite(&AggregatorSuite{})
23
24 func (s *AggregatorSuite) TestInterface(c *check.C) {
25         var _ http.Handler = &Aggregator{}
26 }
27
28 func (s *AggregatorSuite) SetUpTest(c *check.C) {
29         s.handler = &Aggregator{Config: &arvados.Config{
30                 Clusters: map[string]arvados.Cluster{
31                         "zzzzz": {
32                                 ManagementToken: arvadostest.ManagementToken,
33                                 SystemNodes:     map[string]arvados.SystemNode{},
34                         },
35                 },
36         }}
37         s.req = httptest.NewRequest("GET", "/_health/all", nil)
38         s.req.Header.Set("Authorization", "Bearer "+arvadostest.ManagementToken)
39         s.resp = httptest.NewRecorder()
40 }
41
42 func (s *AggregatorSuite) TestNoAuth(c *check.C) {
43         s.req.Header.Del("Authorization")
44         s.handler.ServeHTTP(s.resp, s.req)
45         s.checkError(c)
46         c.Check(s.resp.Code, check.Equals, http.StatusUnauthorized)
47 }
48
49 func (s *AggregatorSuite) TestBadAuth(c *check.C) {
50         s.req.Header.Set("Authorization", "xyzzy")
51         s.handler.ServeHTTP(s.resp, s.req)
52         s.checkError(c)
53         c.Check(s.resp.Code, check.Equals, http.StatusUnauthorized)
54 }
55
56 func (s *AggregatorSuite) TestEmptyConfig(c *check.C) {
57         s.handler.ServeHTTP(s.resp, s.req)
58         s.checkOK(c)
59 }
60
61 func (s *AggregatorSuite) stubServer(handler http.Handler) (*httptest.Server, string) {
62         srv := httptest.NewServer(handler)
63         var port string
64         if parts := strings.Split(srv.URL, ":"); len(parts) < 3 {
65                 panic(srv.URL)
66         } else {
67                 port = parts[len(parts)-1]
68         }
69         return srv, ":" + port
70 }
71
72 type unhealthyHandler struct{}
73
74 func (*unhealthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
75         if req.URL.Path == "/_health/ping" {
76                 resp.Write([]byte(`{"health":"ERROR","error":"the bends"}`))
77         } else {
78                 http.Error(resp, "not found", http.StatusNotFound)
79         }
80 }
81
82 func (s *AggregatorSuite) TestUnhealthy(c *check.C) {
83         srv, listen := s.stubServer(&unhealthyHandler{})
84         defer srv.Close()
85         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
86                 Keepstore: arvados.Keepstore{Listen: listen},
87         }
88         s.handler.ServeHTTP(s.resp, s.req)
89         s.checkUnhealthy(c)
90 }
91
92 type healthyHandler struct{}
93
94 func (*healthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
95         if req.URL.Path == "/_health/ping" {
96                 resp.Write([]byte(`{"health":"OK"}`))
97         } else {
98                 http.Error(resp, "not found", http.StatusNotFound)
99         }
100 }
101
102 func (s *AggregatorSuite) TestHealthy(c *check.C) {
103         srv, listen := s.stubServer(&healthyHandler{})
104         defer srv.Close()
105         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
106                 Keepstore: arvados.Keepstore{Listen: listen},
107         }
108         s.handler.ServeHTTP(s.resp, s.req)
109         resp := s.checkOK(c)
110         ep := resp.Checks["localhost/keepstore/_health/ping"]
111         c.Check(ep.Health, check.Equals, "OK")
112         c.Check(ep.Status, check.Equals, 200)
113 }
114
115 func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
116         srvH, listenH := s.stubServer(&healthyHandler{})
117         defer srvH.Close()
118         srvU, listenU := s.stubServer(&unhealthyHandler{})
119         defer srvU.Close()
120         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
121                 Keepstore: arvados.Keepstore{Listen: listenH},
122         }
123         s.handler.Config.Clusters["zzzzz"].SystemNodes["127.0.0.1"] = arvados.SystemNode{
124                 Keepstore: arvados.Keepstore{Listen: listenU},
125         }
126         s.handler.ServeHTTP(s.resp, s.req)
127         resp := s.checkUnhealthy(c)
128         ep := resp.Checks["localhost/keepstore/_health/ping"]
129         c.Check(ep.Health, check.Equals, "OK")
130         c.Check(ep.Status, check.Equals, 200)
131         ep = resp.Checks["127.0.0.1/keepstore/_health/ping"]
132         c.Check(ep.Health, check.Equals, "ERROR")
133         c.Check(ep.Status, check.Equals, 200)
134 }
135
136 func (s *AggregatorSuite) checkError(c *check.C) {
137         c.Check(s.resp.Code, check.Not(check.Equals), http.StatusOK)
138         var resp ClusterHealthResponse
139         err := json.NewDecoder(s.resp.Body).Decode(&resp)
140         c.Check(err, check.IsNil)
141         c.Check(resp.Health, check.Not(check.Equals), "OK")
142 }
143
144 func (s *AggregatorSuite) checkUnhealthy(c *check.C) ClusterHealthResponse {
145         return s.checkResult(c, "ERROR")
146 }
147
148 func (s *AggregatorSuite) checkOK(c *check.C) ClusterHealthResponse {
149         return s.checkResult(c, "OK")
150 }
151
152 func (s *AggregatorSuite) checkResult(c *check.C, health string) ClusterHealthResponse {
153         c.Check(s.resp.Code, check.Equals, http.StatusOK)
154         var resp ClusterHealthResponse
155         err := json.NewDecoder(s.resp.Body).Decode(&resp)
156         c.Check(err, check.IsNil)
157         c.Check(resp.Health, check.Equals, health)
158         return resp
159 }
160
161 type slowHandler struct{}
162
163 func (*slowHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
164         if req.URL.Path == "/_health/ping" {
165                 time.Sleep(3 * time.Second)
166                 resp.Write([]byte(`{"health":"OK"}`))
167         } else {
168                 http.Error(resp, "not found", http.StatusNotFound)
169         }
170 }
171
172 func (s *AggregatorSuite) TestPingTimeout(c *check.C) {
173         s.handler.timeout = arvados.Duration(100 * time.Millisecond)
174         srv, listen := s.stubServer(&slowHandler{})
175         defer srv.Close()
176         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
177                 Keepstore: arvados.Keepstore{Listen: listen},
178         }
179         s.handler.ServeHTTP(s.resp, s.req)
180         resp := s.checkUnhealthy(c)
181         ep := resp.Checks["localhost/keepstore/_health/ping"]
182         c.Check(ep.Health, check.Equals, "ERROR")
183         c.Check(ep.Status, check.Equals, 0)
184         rt, err := ep.ResponseTime.Float64()
185         c.Check(err, check.IsNil)
186         c.Check(rt > 0.005, check.Equals, true)
187 }