Merge branch '16265-security-updates' into dependabot/bundler/apps/workbench/loofah...
[arvados.git] / services / ws / server_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "encoding/json"
9         "io/ioutil"
10         "net/http"
11         "os"
12         "sync"
13         "time"
14
15         "git.arvados.org/arvados.git/lib/config"
16         "git.arvados.org/arvados.git/sdk/go/arvados"
17         "git.arvados.org/arvados.git/sdk/go/arvadostest"
18         "git.arvados.org/arvados.git/sdk/go/ctxlog"
19         check "gopkg.in/check.v1"
20 )
21
22 var _ = check.Suite(&serverSuite{})
23
24 type serverSuite struct {
25         cluster *arvados.Cluster
26         srv     *server
27         wg      sync.WaitGroup
28 }
29
30 func (s *serverSuite) SetUpTest(c *check.C) {
31         var err error
32         s.cluster, err = s.testConfig(c)
33         c.Assert(err, check.IsNil)
34         s.srv = &server{cluster: s.cluster}
35 }
36
37 func (*serverSuite) testConfig(c *check.C) (*arvados.Cluster, error) {
38         ldr := config.NewLoader(nil, ctxlog.TestLogger(c))
39         cfg, err := ldr.Load()
40         if err != nil {
41                 return nil, err
42         }
43         cluster, err := cfg.GetCluster("")
44         if err != nil {
45                 return nil, err
46         }
47         client := arvados.NewClientFromEnv()
48         cluster.Services.Controller.ExternalURL.Host = client.APIHost
49         cluster.SystemRootToken = client.AuthToken
50         cluster.TLS.Insecure = client.Insecure
51         cluster.PostgreSQL.Connection = testDBConfig()
52         cluster.Services.Websocket.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: ":"}: arvados.ServiceInstance{}}
53         cluster.ManagementToken = arvadostest.ManagementToken
54         return cluster, nil
55 }
56
57 // TestBadDB ensures Run() returns an error (instead of panicking or
58 // deadlocking) if it can't connect to the database server at startup.
59 func (s *serverSuite) TestBadDB(c *check.C) {
60         s.cluster.PostgreSQL.Connection["password"] = "1234"
61
62         var wg sync.WaitGroup
63         wg.Add(1)
64         go func() {
65                 err := s.srv.Run()
66                 c.Check(err, check.NotNil)
67                 wg.Done()
68         }()
69         wg.Add(1)
70         go func() {
71                 s.srv.WaitReady()
72                 wg.Done()
73         }()
74
75         done := make(chan bool)
76         go func() {
77                 wg.Wait()
78                 close(done)
79         }()
80         select {
81         case <-done:
82         case <-time.After(10 * time.Second):
83                 c.Fatal("timeout")
84         }
85 }
86
87 func (s *serverSuite) TestHealth(c *check.C) {
88         go s.srv.Run()
89         defer s.srv.Close()
90         s.srv.WaitReady()
91         for _, token := range []string{"", "foo", s.cluster.ManagementToken} {
92                 req, err := http.NewRequest("GET", "http://"+s.srv.listener.Addr().String()+"/_health/ping", nil)
93                 c.Assert(err, check.IsNil)
94                 if token != "" {
95                         req.Header.Add("Authorization", "Bearer "+token)
96                 }
97                 resp, err := http.DefaultClient.Do(req)
98                 c.Check(err, check.IsNil)
99                 if token == s.cluster.ManagementToken {
100                         c.Check(resp.StatusCode, check.Equals, http.StatusOK)
101                         buf, err := ioutil.ReadAll(resp.Body)
102                         c.Check(err, check.IsNil)
103                         c.Check(string(buf), check.Equals, `{"health":"OK"}`+"\n")
104                 } else {
105                         c.Check(resp.StatusCode, check.Not(check.Equals), http.StatusOK)
106                 }
107         }
108 }
109
110 func (s *serverSuite) TestStatus(c *check.C) {
111         go s.srv.Run()
112         defer s.srv.Close()
113         s.srv.WaitReady()
114         req, err := http.NewRequest("GET", "http://"+s.srv.listener.Addr().String()+"/status.json", nil)
115         c.Assert(err, check.IsNil)
116         resp, err := http.DefaultClient.Do(req)
117         c.Check(err, check.IsNil)
118         c.Check(resp.StatusCode, check.Equals, http.StatusOK)
119         var status map[string]interface{}
120         err = json.NewDecoder(resp.Body).Decode(&status)
121         c.Check(err, check.IsNil)
122         c.Check(status["Version"], check.Not(check.Equals), "")
123 }
124
125 func (s *serverSuite) TestHealthDisabled(c *check.C) {
126         s.cluster.ManagementToken = ""
127
128         go s.srv.Run()
129         defer s.srv.Close()
130         s.srv.WaitReady()
131
132         for _, token := range []string{"", "foo", arvadostest.ManagementToken} {
133                 req, err := http.NewRequest("GET", "http://"+s.srv.listener.Addr().String()+"/_health/ping", nil)
134                 c.Assert(err, check.IsNil)
135                 req.Header.Add("Authorization", "Bearer "+token)
136                 resp, err := http.DefaultClient.Do(req)
137                 c.Check(err, check.IsNil)
138                 c.Check(resp.StatusCode, check.Equals, http.StatusNotFound)
139         }
140 }
141
142 func (s *serverSuite) TestLoadLegacyConfig(c *check.C) {
143         content := []byte(`
144 Client:
145   APIHost: example.com
146   AuthToken: abcdefg
147 Postgres:
148   "dbname": "arvados_production"
149   "user": "arvados"
150   "password": "xyzzy"
151   "host": "localhost"
152   "connect_timeout": "30"
153   "sslmode": "require"
154   "fallback_application_name": "arvados-ws"
155 PostgresPool: 63
156 Listen: ":8765"
157 LogLevel: "debug"
158 LogFormat: "text"
159 PingTimeout: 61s
160 ClientEventQueue: 62
161 ServerEventQueue:  5
162 ManagementToken: qqqqq
163 `)
164         tmpfile, err := ioutil.TempFile("", "example")
165         if err != nil {
166                 c.Error(err)
167         }
168
169         defer os.Remove(tmpfile.Name()) // clean up
170
171         if _, err := tmpfile.Write(content); err != nil {
172                 c.Error(err)
173         }
174         if err := tmpfile.Close(); err != nil {
175                 c.Error(err)
176
177         }
178         cluster := configure(logger(nil), []string{"arvados-ws", "-config", tmpfile.Name()})
179         c.Check(cluster, check.NotNil)
180
181         c.Check(cluster.Services.Controller.ExternalURL, check.Equals, arvados.URL{Scheme: "https", Host: "example.com"})
182         c.Check(cluster.SystemRootToken, check.Equals, "abcdefg")
183
184         c.Check(cluster.PostgreSQL.Connection, check.DeepEquals, arvados.PostgreSQLConnection{
185                 "connect_timeout":           "30",
186                 "dbname":                    "arvados_production",
187                 "fallback_application_name": "arvados-ws",
188                 "host":                      "localhost",
189                 "password":                  "xyzzy",
190                 "sslmode":                   "require",
191                 "user":                      "arvados"})
192         c.Check(cluster.PostgreSQL.ConnectionPool, check.Equals, 63)
193         c.Check(cluster.Services.Websocket.InternalURLs[arvados.URL{Host: ":8765"}], check.NotNil)
194         c.Check(cluster.SystemLogs.LogLevel, check.Equals, "debug")
195         c.Check(cluster.SystemLogs.Format, check.Equals, "text")
196         c.Check(cluster.API.SendTimeout, check.Equals, arvados.Duration(61*time.Second))
197         c.Check(cluster.API.WebsocketClientEventQueue, check.Equals, 62)
198         c.Check(cluster.API.WebsocketServerEventQueue, check.Equals, 5)
199         c.Check(cluster.ManagementToken, check.Equals, "qqqqq")
200 }