12216: Merge branch 'master' into 12216-webdav-list
[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.SystemServiceInstance{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                 Keepproxy:   arvados.SystemServiceInstance{Listen: listen},
107                 Keepstore:   arvados.SystemServiceInstance{Listen: listen},
108                 Keepweb:     arvados.SystemServiceInstance{Listen: listen},
109                 Nodemanager: arvados.SystemServiceInstance{Listen: listen},
110                 RailsAPI:    arvados.SystemServiceInstance{Listen: listen},
111                 Websocket:   arvados.SystemServiceInstance{Listen: listen},
112                 Workbench:   arvados.SystemServiceInstance{Listen: listen},
113         }
114         s.handler.ServeHTTP(s.resp, s.req)
115         resp := s.checkOK(c)
116         svc := "keepstore+http://localhost" + listen + "/_health/ping"
117         c.Logf("%#v", resp)
118         ep := resp.Checks[svc]
119         c.Check(ep.Health, check.Equals, "OK")
120         c.Check(ep.HTTPStatusCode, check.Equals, 200)
121 }
122
123 func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
124         srvH, listenH := s.stubServer(&healthyHandler{})
125         defer srvH.Close()
126         srvU, listenU := s.stubServer(&unhealthyHandler{})
127         defer srvU.Close()
128         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
129                 Keepproxy:   arvados.SystemServiceInstance{Listen: listenH},
130                 Keepstore:   arvados.SystemServiceInstance{Listen: listenH},
131                 Keepweb:     arvados.SystemServiceInstance{Listen: listenH},
132                 Nodemanager: arvados.SystemServiceInstance{Listen: listenH},
133                 RailsAPI:    arvados.SystemServiceInstance{Listen: listenH},
134                 Websocket:   arvados.SystemServiceInstance{Listen: listenH},
135                 Workbench:   arvados.SystemServiceInstance{Listen: listenH},
136         }
137         s.handler.Config.Clusters["zzzzz"].SystemNodes["127.0.0.1"] = arvados.SystemNode{
138                 Keepstore: arvados.SystemServiceInstance{Listen: listenU},
139         }
140         s.handler.ServeHTTP(s.resp, s.req)
141         resp := s.checkUnhealthy(c)
142         ep := resp.Checks["keepstore+http://localhost"+listenH+"/_health/ping"]
143         c.Check(ep.Health, check.Equals, "OK")
144         c.Check(ep.HTTPStatusCode, check.Equals, 200)
145         ep = resp.Checks["keepstore+http://127.0.0.1"+listenU+"/_health/ping"]
146         c.Check(ep.Health, check.Equals, "ERROR")
147         c.Check(ep.HTTPStatusCode, check.Equals, 200)
148         c.Logf("%#v", ep)
149 }
150
151 func (s *AggregatorSuite) checkError(c *check.C) {
152         c.Check(s.resp.Code, check.Not(check.Equals), http.StatusOK)
153         var resp ClusterHealthResponse
154         err := json.NewDecoder(s.resp.Body).Decode(&resp)
155         c.Check(err, check.IsNil)
156         c.Check(resp.Health, check.Not(check.Equals), "OK")
157 }
158
159 func (s *AggregatorSuite) checkUnhealthy(c *check.C) ClusterHealthResponse {
160         return s.checkResult(c, "ERROR")
161 }
162
163 func (s *AggregatorSuite) checkOK(c *check.C) ClusterHealthResponse {
164         return s.checkResult(c, "OK")
165 }
166
167 func (s *AggregatorSuite) checkResult(c *check.C, health string) ClusterHealthResponse {
168         c.Check(s.resp.Code, check.Equals, http.StatusOK)
169         var resp ClusterHealthResponse
170         err := json.NewDecoder(s.resp.Body).Decode(&resp)
171         c.Check(err, check.IsNil)
172         c.Check(resp.Health, check.Equals, health)
173         return resp
174 }
175
176 type slowHandler struct{}
177
178 func (*slowHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
179         if req.URL.Path == "/_health/ping" {
180                 time.Sleep(3 * time.Second)
181                 resp.Write([]byte(`{"health":"OK"}`))
182         } else {
183                 http.Error(resp, "not found", http.StatusNotFound)
184         }
185 }
186
187 func (s *AggregatorSuite) TestPingTimeout(c *check.C) {
188         s.handler.timeout = arvados.Duration(100 * time.Millisecond)
189         srv, listen := s.stubServer(&slowHandler{})
190         defer srv.Close()
191         s.handler.Config.Clusters["zzzzz"].SystemNodes["localhost"] = arvados.SystemNode{
192                 Keepstore: arvados.SystemServiceInstance{Listen: listen},
193         }
194         s.handler.ServeHTTP(s.resp, s.req)
195         resp := s.checkUnhealthy(c)
196         ep := resp.Checks["keepstore+http://localhost"+listen+"/_health/ping"]
197         c.Check(ep.Health, check.Equals, "ERROR")
198         c.Check(ep.HTTPStatusCode, check.Equals, 0)
199         rt, err := ep.ResponseTime.Float64()
200         c.Check(err, check.IsNil)
201         c.Check(rt > 0.005, check.Equals, true)
202 }