X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/0091abfb8041e1f63ba8b7ec9baa7bb57a05bcdb..17014a715c21dd85a02c34b807b8c362c8706cf1:/services/keepproxy/keepproxy_test.go diff --git a/services/keepproxy/keepproxy_test.go b/services/keepproxy/keepproxy_test.go index 5944b2c21d..e3b4e36b63 100644 --- a/services/keepproxy/keepproxy_test.go +++ b/services/keepproxy/keepproxy_test.go @@ -1,11 +1,12 @@ package main import ( - "git.curoverse.com/arvados.git/sdk/go/keepclient" - "git.curoverse.com/arvados.git/sdk/go/arvadosclient" "crypto/md5" "crypto/tls" "fmt" + "git.curoverse.com/arvados.git/sdk/go/arvadosclient" + "git.curoverse.com/arvados.git/sdk/go/arvadostest" + "git.curoverse.com/arvados.git/sdk/go/keepclient" . "gopkg.in/check.v1" "io" "io/ioutil" @@ -13,7 +14,7 @@ import ( "net/http" "net/url" "os" - "os/exec" + "strings" "testing" "time" ) @@ -29,51 +30,39 @@ var _ = Suite(&ServerRequiredSuite{}) // Tests that require the Keep server running type ServerRequiredSuite struct{} -func pythonDir() string { - cwd, _ := os.Getwd() - return fmt.Sprintf("%s/../../sdk/python/tests", cwd) +// Wait (up to 1 second) for keepproxy to listen on a port. This +// avoids a race condition where we hit a "connection refused" error +// because we start testing the proxy too soon. +func waitForListener() { + const ( + ms = 5 + ) + for i := 0; listener == nil && i < 1000; i += ms { + time.Sleep(ms * time.Millisecond) + } + if listener == nil { + log.Fatalf("Timed out waiting for listener to start") + } } -func (s *ServerRequiredSuite) SetUpSuite(c *C) { - cwd, _ := os.Getwd() - defer os.Chdir(cwd) - - os.Chdir(pythonDir()) - { - cmd := exec.Command("python", "run_test_server.py", "start") - stderr, err := cmd.StderrPipe() - if err != nil { - log.Fatalf("Setting up stderr pipe: %s", err) - } - go io.Copy(os.Stderr, stderr) - if err := cmd.Run(); err != nil { - panic(fmt.Sprintf("'python run_test_server.py start' returned error %s", err)) - } - } - { - cmd := exec.Command("python", "run_test_server.py", "start_keep") - stderr, err := cmd.StderrPipe() - if err != nil { - log.Fatalf("Setting up stderr pipe: %s", err) - } - go io.Copy(os.Stderr, stderr) - if err := cmd.Run(); err != nil { - panic(fmt.Sprintf("'python run_test_server.py start_keep' returned error %s", err)) - } +func closeListener() { + if listener != nil { + listener.Close() } +} - os.Setenv("ARVADOS_API_HOST", "localhost:3000") - os.Setenv("ARVADOS_API_TOKEN", "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h") - os.Setenv("ARVADOS_API_HOST_INSECURE", "true") +func (s *ServerRequiredSuite) SetUpSuite(c *C) { + arvadostest.StartAPI() + arvadostest.StartKeep() } -func (s *ServerRequiredSuite) TearDownSuite(c *C) { - cwd, _ := os.Getwd() - defer os.Chdir(cwd) +func (s *ServerRequiredSuite) SetUpTest(c *C) { + arvadostest.ResetEnv() +} - os.Chdir(pythonDir()) - exec.Command("python", "run_test_server.py", "stop_keep").Run() - exec.Command("python", "run_test_server.py", "stop").Run() +func (s *ServerRequiredSuite) TearDownSuite(c *C) { + arvadostest.StopKeep() + arvadostest.StopAPI() } func setupProxyService() { @@ -114,23 +103,37 @@ func setupProxyService() { } } -func runProxy(c *C, args []string, token string, port int) keepclient.KeepClient { - os.Args = append(args, fmt.Sprintf("-listen=:%v", port)) - os.Setenv("ARVADOS_API_TOKEN", "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h") - - go main() - time.Sleep(100 * time.Millisecond) - - os.Setenv("ARVADOS_KEEP_PROXY", fmt.Sprintf("http://localhost:%v", port)) - os.Setenv("ARVADOS_API_TOKEN", token) +func runProxy(c *C, args []string, port int, bogusClientToken bool) keepclient.KeepClient { + if bogusClientToken { + os.Setenv("ARVADOS_API_TOKEN", "bogus-token") + } arv, err := arvadosclient.MakeArvadosClient() - kc, err := keepclient.MakeKeepClient(&arv) + c.Assert(err, Equals, nil) + kc := keepclient.KeepClient{ + Arvados: &arv, + Want_replicas: 2, + Using_proxy: true, + Client: &http.Client{}, + } + kc.SetServiceRoots(map[string]string{ + "proxy": fmt.Sprintf("http://localhost:%v", port), + }) c.Check(kc.Using_proxy, Equals, true) c.Check(len(kc.ServiceRoots()), Equals, 1) - c.Check(kc.ServiceRoots()[0], Equals, fmt.Sprintf("http://localhost:%v", port)) - c.Check(err, Equals, nil) - os.Setenv("ARVADOS_KEEP_PROXY", "") + for _, root := range kc.ServiceRoots() { + c.Check(root, Equals, fmt.Sprintf("http://localhost:%v", port)) + } log.Print("keepclient created") + if bogusClientToken { + arvadostest.ResetEnv() + } + + { + os.Args = append(args, fmt.Sprintf("-listen=:%v", port)) + listener = nil + go main() + } + return kc } @@ -138,7 +141,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) { log.Print("TestPutAndGet start") os.Args = []string{"keepproxy", "-listen=:29950"} - os.Setenv("ARVADOS_API_TOKEN", "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h") + listener = nil go main() time.Sleep(100 * time.Millisecond) @@ -146,16 +149,19 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) { os.Setenv("ARVADOS_EXTERNAL_CLIENT", "true") arv, err := arvadosclient.MakeArvadosClient() + c.Assert(err, Equals, nil) kc, err := keepclient.MakeKeepClient(&arv) + c.Assert(err, Equals, nil) c.Check(kc.Arvados.External, Equals, true) c.Check(kc.Using_proxy, Equals, true) c.Check(len(kc.ServiceRoots()), Equals, 1) - c.Check(kc.ServiceRoots()[0], Equals, "http://localhost:29950") - c.Check(err, Equals, nil) + for _, root := range kc.ServiceRoots() { + c.Check(root, Equals, "http://localhost:29950") + } os.Setenv("ARVADOS_EXTERNAL_CLIENT", "") - log.Print("keepclient created") - defer listener.Close() + waitForListener() + defer closeListener() hash := fmt.Sprintf("%x", md5.Sum([]byte("foo"))) var hash2 string @@ -163,9 +169,25 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) { { _, _, err := kc.Ask(hash) c.Check(err, Equals, keepclient.BlockNotFound) - log.Print("Ask 1") + log.Print("Finished Ask (expected BlockNotFound)") } + { + reader, _, _, err := kc.Get(hash) + c.Check(reader, Equals, nil) + c.Check(err, Equals, keepclient.BlockNotFound) + log.Print("Finished Get (expected BlockNotFound)") + } + + // Note in bug #5309 among other errors keepproxy would set + // Content-Length incorrectly on the 404 BlockNotFound response, this + // would result in a protocol violation that would prevent reuse of the + // connection, which would manifest by the next attempt to use the + // connection (in this case the PutB below) failing. So to test for + // that bug it's necessary to trigger an error response (such as + // BlockNotFound) and then do something else with the same httpClient + // connection. + { var rep int var err error @@ -173,14 +195,14 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) { c.Check(hash2, Matches, fmt.Sprintf(`^%s\+3(\+.+)?$`, hash)) c.Check(rep, Equals, 2) c.Check(err, Equals, nil) - log.Print("PutB") + log.Print("Finished PutB (expected success)") } { blocklen, _, err := kc.Ask(hash2) c.Assert(err, Equals, nil) c.Check(blocklen, Equals, int64(3)) - log.Print("Ask 2") + log.Print("Finished Ask (expected success)") } { @@ -189,19 +211,37 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) { all, err := ioutil.ReadAll(reader) c.Check(all, DeepEquals, []byte("foo")) c.Check(blocklen, Equals, int64(3)) - log.Print("Get") + log.Print("Finished Get (expected success)") + } + + { + var rep int + var err error + hash2, rep, err = kc.PutB([]byte("")) + c.Check(hash2, Matches, `^d41d8cd98f00b204e9800998ecf8427e\+0(\+.+)?$`) + c.Check(rep, Equals, 2) + c.Check(err, Equals, nil) + log.Print("Finished PutB zero block") + } + + { + reader, blocklen, _, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e") + c.Assert(err, Equals, nil) + all, err := ioutil.ReadAll(reader) + c.Check(all, DeepEquals, []byte("")) + c.Check(blocklen, Equals, int64(0)) + log.Print("Finished Get zero block") } log.Print("TestPutAndGet done") } func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) { - log.Print("TestPutAndGet start") - - kc := runProxy(c, []string{"keepproxy"}, "123abc", 29951) - defer listener.Close() + log.Print("TestPutAskGetForbidden start") - log.Print("keepclient created") + kc := runProxy(c, []string{"keepproxy"}, 29951, true) + waitForListener() + defer closeListener() hash := fmt.Sprintf("%x", md5.Sum([]byte("bar"))) @@ -233,14 +273,15 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) { log.Print("Get") } - log.Print("TestPutAndGetForbidden done") + log.Print("TestPutAskGetForbidden done") } func (s *ServerRequiredSuite) TestGetDisabled(c *C) { log.Print("TestGetDisabled start") - kc := runProxy(c, []string{"keepproxy", "-no-get"}, "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h", 29952) - defer listener.Close() + kc := runProxy(c, []string{"keepproxy", "-no-get"}, 29952, false) + waitForListener() + defer closeListener() hash := fmt.Sprintf("%x", md5.Sum([]byte("baz"))) @@ -278,8 +319,9 @@ func (s *ServerRequiredSuite) TestGetDisabled(c *C) { func (s *ServerRequiredSuite) TestPutDisabled(c *C) { log.Print("TestPutDisabled start") - kc := runProxy(c, []string{"keepproxy", "-no-put"}, "4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h", 29953) - defer listener.Close() + kc := runProxy(c, []string{"keepproxy", "-no-put"}, 29953, false) + waitForListener() + defer closeListener() { hash2, rep, err := kc.PutB([]byte("quux")) @@ -291,3 +333,56 @@ func (s *ServerRequiredSuite) TestPutDisabled(c *C) { log.Print("TestPutDisabled done") } + +func (s *ServerRequiredSuite) TestCorsHeaders(c *C) { + runProxy(c, []string{"keepproxy"}, 29954, false) + waitForListener() + defer closeListener() + + { + client := http.Client{} + req, err := http.NewRequest("OPTIONS", + fmt.Sprintf("http://localhost:29954/%x+3", + md5.Sum([]byte("foo"))), + nil) + req.Header.Add("Access-Control-Request-Method", "PUT") + req.Header.Add("Access-Control-Request-Headers", "Authorization, X-Keep-Desired-Replicas") + resp, err := client.Do(req) + c.Check(err, Equals, nil) + c.Check(resp.StatusCode, Equals, 200) + body, err := ioutil.ReadAll(resp.Body) + c.Check(string(body), Equals, "") + c.Check(resp.Header.Get("Access-Control-Allow-Methods"), Equals, "GET, HEAD, POST, PUT, OPTIONS") + c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*") + } + + { + resp, err := http.Get( + fmt.Sprintf("http://localhost:29954/%x+3", + md5.Sum([]byte("foo")))) + c.Check(err, Equals, nil) + c.Check(resp.Header.Get("Access-Control-Allow-Headers"), Equals, "Authorization, Content-Length, Content-Type, X-Keep-Desired-Replicas") + c.Check(resp.Header.Get("Access-Control-Allow-Origin"), Equals, "*") + } +} + +func (s *ServerRequiredSuite) TestPostWithoutHash(c *C) { + runProxy(c, []string{"keepproxy"}, 29955, false) + waitForListener() + defer closeListener() + + { + client := http.Client{} + req, err := http.NewRequest("POST", + "http://localhost:29955/", + strings.NewReader("qux")) + req.Header.Add("Authorization", "OAuth2 4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h") + req.Header.Add("Content-Type", "application/octet-stream") + resp, err := client.Do(req) + c.Check(err, Equals, nil) + body, err := ioutil.ReadAll(resp.Body) + c.Check(err, Equals, nil) + c.Check(string(body), Equals, + fmt.Sprintf("%x+%d", md5.Sum([]byte("qux")), 3)) + } +}