1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
16 "git.arvados.org/arvados.git/lib/config"
17 "git.arvados.org/arvados.git/sdk/go/arvados"
18 "git.arvados.org/arvados.git/sdk/go/arvadostest"
19 "git.arvados.org/arvados.git/sdk/go/ctxlog"
20 "github.com/ghodss/yaml"
24 type AggregatorSuite struct {
27 resp *httptest.ResponseRecorder
30 // Gocheck boilerplate
31 var _ = check.Suite(&AggregatorSuite{})
33 func (s *AggregatorSuite) TestInterface(c *check.C) {
34 var _ http.Handler = &Aggregator{}
37 func (s *AggregatorSuite) SetUpTest(c *check.C) {
38 ldr := config.NewLoader(bytes.NewBufferString(`Clusters: {zzzzz: {}}`), ctxlog.TestLogger(c))
40 cfg, err := ldr.Load()
41 c.Assert(err, check.IsNil)
42 cluster, err := cfg.GetCluster("")
43 c.Assert(err, check.IsNil)
44 cluster.ManagementToken = arvadostest.ManagementToken
45 cluster.SystemRootToken = arvadostest.SystemRootToken
46 cluster.Collections.BlobSigningKey = arvadostest.BlobSigningKey
47 cluster.Volumes["z"] = arvados.Volume{StorageClasses: map[string]bool{"default": true}}
48 s.handler = &Aggregator{Cluster: cluster}
49 s.req = httptest.NewRequest("GET", "/_health/all", nil)
50 s.req.Header.Set("Authorization", "Bearer "+arvadostest.ManagementToken)
51 s.resp = httptest.NewRecorder()
54 func (s *AggregatorSuite) TestNoAuth(c *check.C) {
55 s.req.Header.Del("Authorization")
56 s.handler.ServeHTTP(s.resp, s.req)
58 c.Check(s.resp.Code, check.Equals, http.StatusUnauthorized)
61 func (s *AggregatorSuite) TestBadAuth(c *check.C) {
62 s.req.Header.Set("Authorization", "xyzzy")
63 s.handler.ServeHTTP(s.resp, s.req)
65 c.Check(s.resp.Code, check.Equals, http.StatusUnauthorized)
68 func (s *AggregatorSuite) TestNoServicesConfigured(c *check.C) {
69 s.handler.ServeHTTP(s.resp, s.req)
73 func (s *AggregatorSuite) stubServer(handler http.Handler) (*httptest.Server, string) {
74 srv := httptest.NewServer(handler)
76 if parts := strings.Split(srv.URL, ":"); len(parts) < 3 {
79 port = parts[len(parts)-1]
81 return srv, ":" + port
84 func (s *AggregatorSuite) TestUnhealthy(c *check.C) {
85 srv, listen := s.stubServer(&unhealthyHandler{})
87 arvadostest.SetServiceURL(&s.handler.Cluster.Services.Keepstore, "http://localhost"+listen+"/")
88 s.handler.ServeHTTP(s.resp, s.req)
92 func (s *AggregatorSuite) TestHealthy(c *check.C) {
93 srv, listen := s.stubServer(&healthyHandler{})
95 s.setAllServiceURLs(listen)
96 s.handler.ServeHTTP(s.resp, s.req)
98 svc := "keepstore+http://localhost" + listen + "/_health/ping"
100 ep := resp.Checks[svc]
101 c.Check(ep.Health, check.Equals, "OK")
102 c.Check(ep.HTTPStatusCode, check.Equals, 200)
105 func (s *AggregatorSuite) TestHealthyAndUnhealthy(c *check.C) {
106 srvH, listenH := s.stubServer(&healthyHandler{})
108 srvU, listenU := s.stubServer(&unhealthyHandler{})
110 s.setAllServiceURLs(listenH)
111 arvadostest.SetServiceURL(&s.handler.Cluster.Services.Keepstore, "http://localhost"+listenH+"/", "http://127.0.0.1"+listenU+"/")
112 s.handler.ServeHTTP(s.resp, s.req)
113 resp := s.checkUnhealthy(c)
114 ep := resp.Checks["keepstore+http://localhost"+listenH+"/_health/ping"]
115 c.Check(ep.Health, check.Equals, "OK")
116 c.Check(ep.HTTPStatusCode, check.Equals, 200)
117 ep = resp.Checks["keepstore+http://127.0.0.1"+listenU+"/_health/ping"]
118 c.Check(ep.Health, check.Equals, "ERROR")
119 c.Check(ep.HTTPStatusCode, check.Equals, 200)
123 func (s *AggregatorSuite) TestPingTimeout(c *check.C) {
124 s.handler.timeout = arvados.Duration(100 * time.Millisecond)
125 srv, listen := s.stubServer(&slowHandler{})
127 arvadostest.SetServiceURL(&s.handler.Cluster.Services.Keepstore, "http://localhost"+listen+"/")
128 s.handler.ServeHTTP(s.resp, s.req)
129 resp := s.checkUnhealthy(c)
130 ep := resp.Checks["keepstore+http://localhost"+listen+"/_health/ping"]
131 c.Check(ep.Health, check.Equals, "ERROR")
132 c.Check(ep.HTTPStatusCode, check.Equals, 0)
133 rt, err := ep.ResponseTime.Float64()
134 c.Check(err, check.IsNil)
135 c.Check(rt > 0.005, check.Equals, true)
138 func (s *AggregatorSuite) TestCheckCommand(c *check.C) {
139 srv, listen := s.stubServer(&healthyHandler{})
141 s.setAllServiceURLs(listen)
143 confdata, err := yaml.Marshal(arvados.Config{Clusters: map[string]arvados.Cluster{s.handler.Cluster.ClusterID: *s.handler.Cluster}})
144 c.Assert(err, check.IsNil)
145 err = ioutil.WriteFile(tmpdir+"/config.yml", confdata, 0777)
146 c.Assert(err, check.IsNil)
147 var stdout, stderr bytes.Buffer
148 exitcode := CheckCommand.RunCommand("check", []string{"-config=" + tmpdir + "/config.yml"}, &bytes.Buffer{}, &stdout, &stderr)
149 c.Check(exitcode, check.Equals, 0)
150 c.Check(stderr.String(), check.Equals, "")
151 c.Check(stdout.String(), check.Matches, `(?ms).*(\n|^)health: OK\n.*`)
154 func (s *AggregatorSuite) checkError(c *check.C) {
155 c.Check(s.resp.Code, check.Not(check.Equals), http.StatusOK)
156 var resp ClusterHealthResponse
157 err := json.Unmarshal(s.resp.Body.Bytes(), &resp)
158 c.Check(err, check.IsNil)
159 c.Check(resp.Health, check.Not(check.Equals), "OK")
162 func (s *AggregatorSuite) checkUnhealthy(c *check.C) ClusterHealthResponse {
163 return s.checkResult(c, "ERROR")
166 func (s *AggregatorSuite) checkOK(c *check.C) ClusterHealthResponse {
167 return s.checkResult(c, "OK")
170 func (s *AggregatorSuite) checkResult(c *check.C, health string) ClusterHealthResponse {
171 c.Check(s.resp.Code, check.Equals, http.StatusOK)
172 var resp ClusterHealthResponse
173 c.Log(s.resp.Body.String())
174 err := json.Unmarshal(s.resp.Body.Bytes(), &resp)
175 c.Check(err, check.IsNil)
176 c.Check(resp.Health, check.Equals, health)
180 func (s *AggregatorSuite) setAllServiceURLs(listen string) {
181 svcs := &s.handler.Cluster.Services
182 for _, svc := range []*arvados.Service{
196 arvadostest.SetServiceURL(svc, "http://localhost"+listen+"/")
200 type unhealthyHandler struct{}
202 func (*unhealthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
203 if req.URL.Path == "/_health/ping" {
204 resp.Write([]byte(`{"health":"ERROR","error":"the bends"}`))
206 http.Error(resp, "not found", http.StatusNotFound)
210 type healthyHandler struct{}
212 func (*healthyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
213 if req.URL.Path == "/_health/ping" {
214 resp.Write([]byte(`{"health":"OK"}`))
216 http.Error(resp, "not found", http.StatusNotFound)
220 type slowHandler struct{}
222 func (*slowHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
223 if req.URL.Path == "/_health/ping" {
224 time.Sleep(3 * time.Second)
225 resp.Write([]byte(`{"health":"OK"}`))
227 http.Error(resp, "not found", http.StatusNotFound)