11537: Add Via header to proxied keepstore requests.
[arvados.git] / services / keepproxy / keepproxy_test.go
index 56c9bff91c63d30306ed7b08f3fa08b215e7479f..e4ba22a5c4a4218f93d134f190db674de789a3dc 100644 (file)
@@ -1,19 +1,23 @@
 package main
 
 import (
+       "bytes"
        "crypto/md5"
+       "errors"
        "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"
        "io/ioutil"
        "log"
        "net/http"
+       "net/http/httptest"
        "os"
        "strings"
        "testing"
        "time"
 
+       "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"
 )
 
@@ -43,7 +47,7 @@ func waitForListener() {
        const (
                ms = 5
        )
-       for i := 0; listener == nil && i < 1000; i += ms {
+       for i := 0; listener == nil && i < 10000; i += ms {
                time.Sleep(ms * time.Millisecond)
        }
        if listener == nil {
@@ -99,7 +103,7 @@ func runProxy(c *C, args []string, bogusClientToken bool) *keepclient.KeepClient
        if bogusClientToken {
                arv.ApiToken = "bogus-token"
        }
-       kc := keepclient.New(&arv)
+       kc := keepclient.New(arv)
        sr := map[string]string{
                TestProxyUUID: "http://" + listener.Addr().String(),
        }
@@ -109,6 +113,80 @@ func runProxy(c *C, args []string, bogusClientToken bool) *keepclient.KeepClient
        return kc
 }
 
+func (s *ServerRequiredSuite) TestLoopDetection(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+
+       sr := map[string]string{
+               TestProxyUUID: "http://" + listener.Addr().String(),
+       }
+       router.(*proxyHandler).KeepClient.SetServiceRoots(sr, sr, sr)
+
+       content := []byte("TestLoopDetection")
+       _, _, err := kc.PutB(content)
+       c.Check(err, ErrorMatches, `.*loop detected.*`)
+
+       hash := fmt.Sprintf("%x", md5.Sum(content))
+       _, _, _, err = kc.Get(hash)
+       c.Check(err, ErrorMatches, `.*loop detected.*`)
+}
+
+func (s *ServerRequiredSuite) TestDesiredReplicas(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+
+       content := []byte("TestDesiredReplicas")
+       hash := fmt.Sprintf("%x", md5.Sum(content))
+
+       for _, kc.Want_replicas = range []int{0, 1, 2} {
+               locator, rep, err := kc.PutB(content)
+               c.Check(err, Equals, nil)
+               c.Check(rep, Equals, kc.Want_replicas)
+               if rep > 0 {
+                       c.Check(locator, Matches, fmt.Sprintf(`^%s\+%d(\+.+)?$`, hash, len(content)))
+               }
+       }
+}
+
+func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+
+       content := []byte("TestPutWrongContentLength")
+       hash := fmt.Sprintf("%x", md5.Sum(content))
+
+       // If we use http.Client to send these requests to the network
+       // server we just started, the Go http library automatically
+       // fixes the invalid Content-Length header. In order to test
+       // our server behavior, we have to call the handler directly
+       // using an httptest.ResponseRecorder.
+       rtr := MakeRESTRouter(true, true, kc)
+
+       type testcase struct {
+               sendLength   string
+               expectStatus int
+       }
+
+       for _, t := range []testcase{
+               {"1", http.StatusBadRequest},
+               {"", http.StatusLengthRequired},
+               {"-1", http.StatusLengthRequired},
+               {"abcdef", http.StatusLengthRequired},
+       } {
+               req, err := http.NewRequest("PUT",
+                       fmt.Sprintf("http://%s/%s+%d", listener.Addr().String(), hash, len(content)),
+                       bytes.NewReader(content))
+               c.Assert(err, IsNil)
+               req.Header.Set("Content-Length", t.sendLength)
+               req.Header.Set("Authorization", "OAuth2 4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h")
+               req.Header.Set("Content-Type", "application/octet-stream")
+
+               resp := httptest.NewRecorder()
+               rtr.ServeHTTP(resp, req)
+               c.Check(resp.Code, Equals, t.expectStatus)
+       }
+}
+
 func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
        kc := runProxy(c, nil, false)
        defer closeListener()
@@ -202,7 +280,7 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
                hash2, rep, err := kc.PutB([]byte("bar"))
                c.Check(hash2, Equals, "")
                c.Check(rep, Equals, 0)
-               c.Check(err, Equals, keepclient.InsufficientReplicasError)
+               c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
                log.Print("PutB")
        }
 
@@ -273,7 +351,7 @@ func (s *ServerRequiredSuite) TestPutDisabled(c *C) {
        hash2, rep, err := kc.PutB([]byte("quux"))
        c.Check(hash2, Equals, "")
        c.Check(rep, Equals, 0)
-       c.Check(err, Equals, keepclient.InsufficientReplicasError)
+       c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
 }
 
 func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
@@ -446,7 +524,7 @@ func (s *ServerRequiredSuite) TestAskGetKeepProxyConnectionError(c *C) {
        c.Assert(err, Equals, nil)
 
        // keepclient with no such keep server
-       kc := keepclient.New(&arv)
+       kc := keepclient.New(arv)
        locals := map[string]string{
                TestProxyUUID: "http://localhost:12345",
        }