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