1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "git.arvados.org/arvados.git/lib/config"
20 "git.arvados.org/arvados.git/lib/service"
21 "git.arvados.org/arvados.git/sdk/go/arvados"
22 "git.arvados.org/arvados.git/sdk/go/arvadostest"
23 "git.arvados.org/arvados.git/sdk/go/ctxlog"
24 "git.arvados.org/arvados.git/sdk/go/httpserver"
25 "github.com/prometheus/client_golang/prometheus"
26 "github.com/sirupsen/logrus"
27 check "gopkg.in/check.v1"
30 var _ = check.Suite(&serviceSuite{})
32 type serviceSuite struct {
33 handler service.Handler
34 reg *prometheus.Registry
36 cluster *arvados.Cluster
40 func (s *serviceSuite) SetUpTest(c *check.C) {
42 s.cluster, err = s.testConfig(c)
43 c.Assert(err, check.IsNil)
46 func (s *serviceSuite) start(c *check.C) {
47 s.reg = prometheus.NewRegistry()
48 s.handler = newHandler(context.Background(), s.cluster, "", s.reg)
49 instrumented := httpserver.Instrument(s.reg, ctxlog.TestLogger(c), s.handler)
50 s.srv = httptest.NewServer(instrumented.ServeAPI(s.cluster.ManagementToken, instrumented))
53 func (s *serviceSuite) TearDownTest(c *check.C) {
59 func (*serviceSuite) testConfig(c *check.C) (*arvados.Cluster, error) {
60 ldr := config.NewLoader(nil, ctxlog.TestLogger(c))
61 cfg, err := ldr.Load()
65 cluster, err := cfg.GetCluster("")
69 client := arvados.NewClientFromEnv()
70 cluster.Services.Controller.ExternalURL.Host = client.APIHost
71 cluster.SystemRootToken = client.AuthToken
72 cluster.TLS.Insecure = client.Insecure
73 cluster.PostgreSQL.Connection = testDBConfig()
74 cluster.PostgreSQL.ConnectionPool = 12
75 cluster.Services.Websocket.InternalURLs = map[arvados.URL]arvados.ServiceInstance{{Host: ":"}: {}}
76 cluster.ManagementToken = arvadostest.ManagementToken
80 // TestBadDB ensures the server returns an error (instead of panicking
81 // or deadlocking) if it can't connect to the database server at
83 func (s *serviceSuite) TestBadDB(c *check.C) {
84 s.cluster.PostgreSQL.Connection["password"] = "1234"
86 resp, err := http.Get(s.srv.URL)
87 c.Check(err, check.IsNil)
88 c.Check(resp.StatusCode, check.Equals, http.StatusInternalServerError)
89 c.Check(s.handler.CheckHealth(), check.ErrorMatches, "database not connected")
90 c.Check(err, check.IsNil)
91 c.Check(resp.StatusCode, check.Equals, http.StatusInternalServerError)
94 func (s *serviceSuite) TestHealth(c *check.C) {
96 for _, token := range []string{"", "foo", s.cluster.ManagementToken} {
97 req, err := http.NewRequest("GET", s.srv.URL+"/_health/ping", nil)
98 c.Assert(err, check.IsNil)
100 req.Header.Add("Authorization", "Bearer "+token)
102 resp, err := http.DefaultClient.Do(req)
103 c.Check(err, check.IsNil)
104 if token == s.cluster.ManagementToken {
105 c.Check(resp.StatusCode, check.Equals, http.StatusOK)
106 buf, err := ioutil.ReadAll(resp.Body)
107 c.Check(err, check.IsNil)
108 c.Check(string(buf), check.Equals, `{"health":"OK"}`+"\n")
110 c.Check(resp.StatusCode, check.Not(check.Equals), http.StatusOK)
115 func (s *serviceSuite) TestMetrics(c *check.C) {
117 s.handler.CheckHealth()
118 for deadline := time.Now().Add(time.Second); ; {
119 req, err := http.NewRequest("GET", s.srv.URL+"/metrics", nil)
120 c.Assert(err, check.IsNil)
121 req.Header.Set("Authorization", "Bearer "+s.cluster.ManagementToken)
122 resp, err := http.DefaultClient.Do(req)
123 c.Check(err, check.IsNil)
124 c.Check(resp.StatusCode, check.Equals, http.StatusOK)
125 text, err := ioutil.ReadAll(resp.Body)
126 c.Check(err, check.IsNil)
127 if strings.Contains(string(text), "_db_max_connections 0\n") {
128 // wait for the first db stats update
129 if time.Now().After(deadline) {
132 time.Sleep(time.Second / 50)
135 c.Check(string(text), check.Matches, `(?ms).*\narvados_ws_db_max_connections 12\n.*`)
136 c.Check(string(text), check.Matches, `(?ms).*\narvados_ws_db_open_connections\{inuse="0"\} \d+\n.*`)
137 c.Check(string(text), check.Matches, `(?ms).*\narvados_ws_db_open_connections\{inuse="1"\} \d+\n.*`)
142 func (s *serviceSuite) TestHealthDisabled(c *check.C) {
143 s.cluster.ManagementToken = ""
145 for _, token := range []string{"", "foo", arvadostest.ManagementToken} {
146 req, err := http.NewRequest("GET", s.srv.URL+"/_health/ping", nil)
147 c.Assert(err, check.IsNil)
148 req.Header.Add("Authorization", "Bearer "+token)
149 resp, err := http.DefaultClient.Do(req)
150 c.Check(err, check.IsNil)
151 c.Check(resp.StatusCode, check.Equals, http.StatusNotFound)
155 func (s *serviceSuite) TestLoadLegacyConfig(c *check.C) {
161 "dbname": "arvados_production"
165 "connect_timeout": "30"
167 "fallback_application_name": "arvados-ws"
175 ManagementToken: qqqqq
177 tmpfile, err := ioutil.TempFile("", "example")
182 defer os.Remove(tmpfile.Name()) // clean up
184 if _, err := tmpfile.Write(content); err != nil {
187 if err := tmpfile.Close(); err != nil {
191 ldr := config.NewLoader(&bytes.Buffer{}, logrus.New())
192 flagset := flag.NewFlagSet("", flag.ContinueOnError)
193 ldr.SetupFlags(flagset)
194 flagset.Parse(ldr.MungeLegacyConfigArgs(ctxlog.TestLogger(c), []string{"-config", tmpfile.Name()}, "-legacy-ws-config"))
195 cfg, err := ldr.Load()
196 c.Check(err, check.IsNil)
197 cluster, err := cfg.GetCluster("")
198 c.Check(err, check.IsNil)
199 c.Check(cluster, check.NotNil)
201 c.Check(cluster.Services.Controller.ExternalURL, check.Equals, arvados.URL{Scheme: "https", Host: "example.com", Path: "/"})
202 c.Check(cluster.SystemRootToken, check.Equals, "abcdefg")
204 c.Check(cluster.PostgreSQL.Connection, check.DeepEquals, arvados.PostgreSQLConnection{
205 "connect_timeout": "30",
206 "dbname": "arvados_production",
207 "fallback_application_name": "arvados-ws",
210 "sslmode": "require",
212 c.Check(cluster.PostgreSQL.ConnectionPool, check.Equals, 63)
213 c.Check(cluster.Services.Websocket.InternalURLs[arvados.URL{Host: ":8765"}], check.NotNil)
214 c.Check(cluster.SystemLogs.LogLevel, check.Equals, "debug")
215 c.Check(cluster.SystemLogs.Format, check.Equals, "text")
216 c.Check(cluster.API.SendTimeout, check.Equals, arvados.Duration(61*time.Second))
217 c.Check(cluster.API.WebsocketClientEventQueue, check.Equals, 62)
218 c.Check(cluster.API.WebsocketServerEventQueue, check.Equals, 5)
219 c.Check(cluster.ManagementToken, check.Equals, "qqqqq")