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