Merge branch 'master' into 11850-singlecontainer-max-requirements
[arvados.git] / services / keepproxy / keepproxy_test.go
index 4e856262dd1827395df6c54c99a5c68cfb18432a..de72e747f3fa10c1cc1c3d64839ec317c29d42e3 100644 (file)
@@ -1,3 +1,7 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 package main
 
 import (
@@ -6,11 +10,12 @@ import (
        "errors"
        "fmt"
        "io/ioutil"
-       "log"
+       "math/rand"
        "net/http"
        "net/http/httptest"
        "os"
        "strings"
+       "sync"
        "testing"
        "time"
 
@@ -51,7 +56,7 @@ func waitForListener() {
                time.Sleep(ms * time.Millisecond)
        }
        if listener == nil {
-               log.Fatalf("Timed out waiting for listener to start")
+               panic("Timed out waiting for listener to start")
        }
 }
 
@@ -185,7 +190,7 @@ func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
        // 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)
+       rtr := MakeRESTRouter(true, true, kc, 10*time.Second, "")
 
        type testcase struct {
                sendLength   string
@@ -212,6 +217,33 @@ func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
        }
 }
 
+func (s *ServerRequiredSuite) TestManyFailedPuts(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+       router.(*proxyHandler).timeout = time.Nanosecond
+
+       buf := make([]byte, 1<<20)
+       rand.Read(buf)
+       var wg sync.WaitGroup
+       for i := 0; i < 128; i++ {
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+                       kc.PutB(buf)
+               }()
+       }
+       done := make(chan bool)
+       go func() {
+               wg.Wait()
+               close(done)
+       }()
+       select {
+       case <-done:
+       case <-time.After(10 * time.Second):
+               c.Error("timeout")
+       }
+}
+
 func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
        kc := runProxy(c, nil, false)
        defer closeListener()
@@ -222,14 +254,14 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
        {
                _, _, err := kc.Ask(hash)
                c.Check(err, Equals, keepclient.BlockNotFound)
-               log.Print("Finished Ask (expected BlockNotFound)")
+               c.Log("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)")
+               c.Log("Finished Get (expected BlockNotFound)")
        }
 
        // Note in bug #5309 among other errors keepproxy would set
@@ -248,14 +280,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("Finished PutB (expected success)")
+               c.Log("Finished PutB (expected success)")
        }
 
        {
                blocklen, _, err := kc.Ask(hash2)
                c.Assert(err, Equals, nil)
                c.Check(blocklen, Equals, int64(3))
-               log.Print("Finished Ask (expected success)")
+               c.Log("Finished Ask (expected success)")
        }
 
        {
@@ -264,7 +296,7 @@ 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("Finished Get (expected success)")
+               c.Log("Finished Get (expected success)")
        }
 
        {
@@ -274,7 +306,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
                c.Check(hash2, Matches, `^d41d8cd98f00b204e9800998ecf8427e\+0(\+.+)?$`)
                c.Check(rep, Equals, 2)
                c.Check(err, Equals, nil)
-               log.Print("Finished PutB zero block")
+               c.Log("Finished PutB zero block")
        }
 
        {
@@ -283,7 +315,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
                all, err := ioutil.ReadAll(reader)
                c.Check(all, DeepEquals, []byte(""))
                c.Check(blocklen, Equals, int64(0))
-               log.Print("Finished Get zero block")
+               c.Log("Finished Get zero block")
        }
 }
 
@@ -291,41 +323,26 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
        kc := runProxy(c, nil, true)
        defer closeListener()
 
-       hash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
+       hash := fmt.Sprintf("%x+3", md5.Sum([]byte("bar")))
 
-       {
-               _, _, err := kc.Ask(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
-               log.Print("Ask 1")
-       }
+       _, _, err := kc.Ask(hash)
+       c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
 
-       {
-               hash2, rep, err := kc.PutB([]byte("bar"))
-               c.Check(hash2, Equals, "")
-               c.Check(rep, Equals, 0)
-               c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
-               log.Print("PutB")
-       }
+       hash2, rep, err := kc.PutB([]byte("bar"))
+       c.Check(hash2, Equals, "")
+       c.Check(rep, Equals, 0)
+       c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
 
-       {
-               blocklen, _, err := kc.Ask(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
-               c.Check(blocklen, Equals, int64(0))
-               log.Print("Ask 2")
-       }
+       blocklen, _, err := kc.Ask(hash)
+       c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
+       c.Check(err, ErrorMatches, ".*not found.*")
+       c.Check(blocklen, Equals, int64(0))
+
+       _, blocklen, _, err = kc.Get(hash)
+       c.Check(err, FitsTypeOf, &keepclient.ErrNotFound{})
+       c.Check(err, ErrorMatches, ".*not found.*")
+       c.Check(blocklen, Equals, int64(0))
 
-       {
-               _, blocklen, _, err := kc.Get(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
-               c.Check(blocklen, Equals, int64(0))
-               log.Print("Get")
-       }
 }
 
 func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
@@ -338,8 +355,8 @@ func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
                _, _, err := kc.Ask(hash)
                errNotFound, _ := err.(keepclient.ErrNotFound)
                c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 400"), Equals, true)
-               log.Print("Ask 1")
+               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
+               c.Log("Ask 1")
        }
 
        {
@@ -347,25 +364,25 @@ func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
                c.Check(hash2, Matches, fmt.Sprintf(`^%s\+3(\+.+)?$`, hash))
                c.Check(rep, Equals, 2)
                c.Check(err, Equals, nil)
-               log.Print("PutB")
+               c.Log("PutB")
        }
 
        {
                blocklen, _, err := kc.Ask(hash)
                errNotFound, _ := err.(keepclient.ErrNotFound)
                c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 400"), Equals, true)
+               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
                c.Check(blocklen, Equals, int64(0))
-               log.Print("Ask 2")
+               c.Log("Ask 2")
        }
 
        {
                _, blocklen, _, err := kc.Get(hash)
                errNotFound, _ := err.(keepclient.ErrNotFound)
                c.Check(errNotFound, NotNil)
-               c.Assert(strings.Contains(err.Error(), "HTTP 400"), Equals, true)
+               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
                c.Check(blocklen, Equals, int64(0))
-               log.Print("Get")
+               c.Log("Get")
        }
 }
 
@@ -512,35 +529,53 @@ func (s *ServerRequiredSuite) TestGetIndex(c *C) {
        c.Assert((err != nil), Equals, true)
 }
 
+func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+       hash, _, err := kc.PutB([]byte("shareddata"))
+       c.Check(err, IsNil)
+       kc.Arvados.ApiToken = arvadostest.FooCollectionSharingToken
+       rdr, _, _, err := kc.Get(hash)
+       c.Assert(err, IsNil)
+       data, err := ioutil.ReadAll(rdr)
+       c.Check(err, IsNil)
+       c.Check(data, DeepEquals, []byte("shareddata"))
+}
+
 func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
        kc := runProxy(c, nil, false)
        defer closeListener()
 
        // Put a test block
        hash, rep, err := kc.PutB([]byte("foo"))
-       c.Check(err, Equals, nil)
+       c.Check(err, IsNil)
        c.Check(rep, Equals, 2)
 
-       for _, token := range []string{
+       for _, badToken := range []string{
                "nosuchtoken",
                "2ym314ysp27sk7h943q6vtc378srb06se3pq6ghurylyf3pdmx", // expired
        } {
-               // Change token to given bad token
-               kc.Arvados.ApiToken = token
+               kc.Arvados.ApiToken = badToken
+
+               // Ask and Get will fail only if the upstream
+               // keepstore server checks for valid signatures.
+               // Without knowing the blob signing key, there is no
+               // way for keepproxy to know whether a given token is
+               // permitted to read a block.  So these tests fail:
+               if false {
+                       _, _, err = kc.Ask(hash)
+                       c.Assert(err, FitsTypeOf, &keepclient.ErrNotFound{})
+                       c.Check(err.(*keepclient.ErrNotFound).Temporary(), Equals, false)
+                       c.Check(err, ErrorMatches, ".*HTTP 403.*")
+
+                       _, _, _, err = kc.Get(hash)
+                       c.Assert(err, FitsTypeOf, &keepclient.ErrNotFound{})
+                       c.Check(err.(*keepclient.ErrNotFound).Temporary(), Equals, false)
+                       c.Check(err, ErrorMatches, ".*HTTP 403 \"Missing or invalid Authorization header\".*")
+               }
 
-               // Ask should result in error
-               _, _, err = kc.Ask(hash)
-               c.Check(err, NotNil)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound.Temporary(), Equals, false)
-               c.Assert(strings.Contains(err.Error(), "HTTP 403"), Equals, true)
-
-               // Get should result in error
-               _, _, _, err = kc.Get(hash)
-               c.Check(err, NotNil)
-               errNotFound, _ = err.(keepclient.ErrNotFound)
-               c.Check(errNotFound.Temporary(), Equals, false)
-               c.Assert(strings.Contains(err.Error(), "HTTP 403 \"Missing or invalid Authorization header\""), Equals, true)
+               _, _, err = kc.PutB([]byte("foo"))
+               c.Check(err, ErrorMatches, ".*403.*Missing or invalid Authorization header")
        }
 }
 
@@ -561,14 +596,14 @@ func (s *ServerRequiredSuite) TestAskGetKeepProxyConnectionError(c *C) {
        c.Check(err, NotNil)
        errNotFound, _ := err.(*keepclient.ErrNotFound)
        c.Check(errNotFound.Temporary(), Equals, true)
-       c.Assert(strings.Contains(err.Error(), "connection refused"), Equals, true)
+       c.Assert(err, ErrorMatches, ".*connection refused.*")
 
        // Get should result in temporary connection refused error
        _, _, _, err = kc.Get(hash)
        c.Check(err, NotNil)
        errNotFound, _ = err.(*keepclient.ErrNotFound)
        c.Check(errNotFound.Temporary(), Equals, true)
-       c.Assert(strings.Contains(err.Error(), "connection refused"), Equals, true)
+       c.Assert(err, ErrorMatches, ".*connection refused.*")
 }
 
 func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
@@ -593,3 +628,21 @@ func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
                c.Check(err, ErrorMatches, `.*HTTP 502.*`)
        }
 }
+
+func (s *ServerRequiredSuite) TestPing(c *C) {
+       kc := runProxy(c, nil, false)
+       defer closeListener()
+
+       rtr := MakeRESTRouter(true, true, kc, 10*time.Second, arvadostest.ManagementToken)
+
+       req, err := http.NewRequest("GET",
+               "http://"+listener.Addr().String()+"/_health/ping",
+               nil)
+       c.Assert(err, IsNil)
+       req.Header.Set("Authorization", "Bearer "+arvadostest.ManagementToken)
+
+       resp := httptest.NewRecorder()
+       rtr.ServeHTTP(resp, req)
+       c.Check(resp.Code, Equals, 200)
+       c.Assert(resp.Body.String(), Matches, `{"health":"OK"}\n?`)
+}