Merge branch 'patch-1' of https://github.com/mr-c/arvados into mr-c-patch-1
[arvados.git] / services / keepproxy / keepproxy_test.go
index 1b9f627b55973384203978de8784fb7e4a9ee3b4..94ed05bff1dbdb0585226741a792d2b77fa7fd29 100644 (file)
@@ -13,15 +13,18 @@ import (
        "math/rand"
        "net/http"
        "net/http/httptest"
-       "os"
        "strings"
        "sync"
        "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"
+       "git.arvados.org/arvados.git/lib/config"
+       "git.arvados.org/arvados.git/sdk/go/arvados"
+       "git.arvados.org/arvados.git/sdk/go/arvadosclient"
+       "git.arvados.org/arvados.git/sdk/go/arvadostest"
+       "git.arvados.org/arvados.git/sdk/go/ctxlog"
+       "git.arvados.org/arvados.git/sdk/go/keepclient"
+       log "github.com/sirupsen/logrus"
 
        . "gopkg.in/check.v1"
 )
@@ -37,6 +40,12 @@ var _ = Suite(&ServerRequiredSuite{})
 // Tests that require the Keep server running
 type ServerRequiredSuite struct{}
 
+// Gocheck boilerplate
+var _ = Suite(&ServerRequiredConfigYmlSuite{})
+
+// Tests that require the Keep servers running as defined in config.yml
+type ServerRequiredConfigYmlSuite struct{}
+
 // Gocheck boilerplate
 var _ = Suite(&NoKeepServerSuite{})
 
@@ -80,6 +89,21 @@ func (s *ServerRequiredSuite) TearDownSuite(c *C) {
        arvadostest.StopAPI()
 }
 
+func (s *ServerRequiredConfigYmlSuite) SetUpSuite(c *C) {
+       arvadostest.StartAPI()
+       // config.yml defines 4 keepstores
+       arvadostest.StartKeep(4, false)
+}
+
+func (s *ServerRequiredConfigYmlSuite) SetUpTest(c *C) {
+       arvadostest.ResetEnv()
+}
+
+func (s *ServerRequiredConfigYmlSuite) TearDownSuite(c *C) {
+       arvadostest.StopKeep(4)
+       arvadostest.StopAPI()
+}
+
 func (s *NoKeepServerSuite) SetUpSuite(c *C) {
        arvadostest.StartAPI()
        // We need API to have some keep services listed, but the
@@ -96,14 +120,28 @@ func (s *NoKeepServerSuite) TearDownSuite(c *C) {
        arvadostest.StopAPI()
 }
 
-func runProxy(c *C, args []string, bogusClientToken bool) *keepclient.KeepClient {
-       args = append([]string{"keepproxy"}, args...)
-       os.Args = append(args, "-listen=:0")
+func runProxy(c *C, bogusClientToken bool, loadKeepstoresFromConfig bool) *keepclient.KeepClient {
+       cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
+       c.Assert(err, Equals, nil)
+       cluster, err := cfg.GetCluster("")
+       c.Assert(err, Equals, nil)
+
+       if !loadKeepstoresFromConfig {
+               // Do not load Keepstore InternalURLs from the config file
+               cluster.Services.Keepstore.InternalURLs = make(map[arvados.URL]arvados.ServiceInstance)
+       }
+
+       cluster.Services.Keepproxy.InternalURLs = map[arvados.URL]arvados.ServiceInstance{arvados.URL{Host: ":0"}: arvados.ServiceInstance{}}
+
        listener = nil
-       go main()
+       go func() {
+               run(log.New(), cluster)
+               defer closeListener()
+       }()
        waitForListener()
 
-       arv, err := arvadosclient.MakeArvadosClient()
+       client := arvados.NewClientFromEnv()
+       arv, err := arvadosclient.New(client)
        c.Assert(err, Equals, nil)
        if bogusClientToken {
                arv.ApiToken = "bogus-token"
@@ -119,16 +157,18 @@ func runProxy(c *C, args []string, bogusClientToken bool) *keepclient.KeepClient
 }
 
 func (s *ServerRequiredSuite) TestResponseViaHeader(c *C) {
-       runProxy(c, nil, false)
+       runProxy(c, false, false)
        defer closeListener()
 
        req, err := http.NewRequest("POST",
                "http://"+listener.Addr().String()+"/",
                strings.NewReader("TestViaHeader"))
+       c.Assert(err, Equals, nil)
        req.Header.Add("Authorization", "OAuth2 "+arvadostest.ActiveToken)
        resp, err := (&http.Client{}).Do(req)
        c.Assert(err, Equals, nil)
        c.Check(resp.Header.Get("Via"), Equals, "HTTP/1.1 keepproxy")
+       c.Assert(resp.StatusCode, Equals, http.StatusOK)
        locator, err := ioutil.ReadAll(resp.Body)
        c.Assert(err, Equals, nil)
        resp.Body.Close()
@@ -144,7 +184,7 @@ func (s *ServerRequiredSuite) TestResponseViaHeader(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestLoopDetection(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        sr := map[string]string{
@@ -161,8 +201,35 @@ func (s *ServerRequiredSuite) TestLoopDetection(c *C) {
        c.Check(err, ErrorMatches, `.*loop detected.*`)
 }
 
+func (s *ServerRequiredSuite) TestStorageClassesHeader(c *C) {
+       kc := runProxy(c, false, false)
+       defer closeListener()
+
+       // Set up fake keepstore to record request headers
+       var hdr http.Header
+       ts := httptest.NewServer(http.HandlerFunc(
+               func(w http.ResponseWriter, r *http.Request) {
+                       hdr = r.Header
+                       http.Error(w, "Error", http.StatusInternalServerError)
+               }))
+       defer ts.Close()
+
+       // Point keepproxy router's keepclient to the fake keepstore
+       sr := map[string]string{
+               TestProxyUUID: ts.URL,
+       }
+       router.(*proxyHandler).KeepClient.SetServiceRoots(sr, sr, sr)
+
+       // Set up client to ask for storage classes to keepproxy
+       kc.StorageClasses = []string{"secure"}
+       content := []byte("Very important data")
+       _, _, err := kc.PutB(content)
+       c.Check(err, NotNil)
+       c.Check(hdr.Get("X-Keep-Storage-Classes"), Equals, "secure")
+}
+
 func (s *ServerRequiredSuite) TestDesiredReplicas(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        content := []byte("TestDesiredReplicas")
@@ -179,7 +246,7 @@ func (s *ServerRequiredSuite) TestDesiredReplicas(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        content := []byte("TestPutWrongContentLength")
@@ -190,7 +257,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, 10*time.Second, "")
+       rtr := MakeRESTRouter(kc, 10*time.Second, "")
 
        type testcase struct {
                sendLength   string
@@ -218,7 +285,7 @@ func (s *ServerRequiredSuite) TestPutWrongContentLength(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestManyFailedPuts(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
        router.(*proxyHandler).timeout = time.Nanosecond
 
@@ -245,7 +312,7 @@ func (s *ServerRequiredSuite) TestManyFailedPuts(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
@@ -322,7 +389,7 @@ func (s *ServerRequiredSuite) TestPutAskGet(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
-       kc := runProxy(c, nil, true)
+       kc := runProxy(c, true, false)
        defer closeListener()
 
        hash := fmt.Sprintf("%x+3", md5.Sum([]byte("bar")))
@@ -347,59 +414,8 @@ func (s *ServerRequiredSuite) TestPutAskGetForbidden(c *C) {
 
 }
 
-func (s *ServerRequiredSuite) TestGetDisabled(c *C) {
-       kc := runProxy(c, []string{"-no-get"}, false)
-       defer closeListener()
-
-       hash := fmt.Sprintf("%x", md5.Sum([]byte("baz")))
-
-       {
-               _, _, err := kc.Ask(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
-               c.Log("Ask 1")
-       }
-
-       {
-               hash2, rep, err := kc.PutB([]byte("baz"))
-               c.Check(hash2, Matches, fmt.Sprintf(`^%s\+3(\+.+)?$`, hash))
-               c.Check(rep, Equals, 2)
-               c.Check(err, Equals, nil)
-               c.Log("PutB")
-       }
-
-       {
-               blocklen, _, err := kc.Ask(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
-               c.Check(blocklen, Equals, int64(0))
-               c.Log("Ask 2")
-       }
-
-       {
-               _, blocklen, _, err := kc.Get(hash)
-               errNotFound, _ := err.(keepclient.ErrNotFound)
-               c.Check(errNotFound, NotNil)
-               c.Assert(err, ErrorMatches, `.*HTTP 405.*`)
-               c.Check(blocklen, Equals, int64(0))
-               c.Log("Get")
-       }
-}
-
-func (s *ServerRequiredSuite) TestPutDisabled(c *C) {
-       kc := runProxy(c, []string{"-no-put"}, false)
-       defer closeListener()
-
-       hash2, rep, err := kc.PutB([]byte("quux"))
-       c.Check(hash2, Equals, "")
-       c.Check(rep, Equals, 0)
-       c.Check(err, FitsTypeOf, keepclient.InsufficientReplicasError(errors.New("")))
-}
-
 func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
-       runProxy(c, nil, false)
+       runProxy(c, false, false)
        defer closeListener()
 
        {
@@ -407,6 +423,7 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
                req, err := http.NewRequest("OPTIONS",
                        fmt.Sprintf("http://%s/%x+3", listener.Addr().String(), md5.Sum([]byte("foo"))),
                        nil)
+               c.Assert(err, IsNil)
                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)
@@ -429,7 +446,7 @@ func (s *ServerRequiredSuite) TestCorsHeaders(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPostWithoutHash(c *C) {
-       runProxy(c, nil, false)
+       runProxy(c, false, false)
        defer closeListener()
 
        {
@@ -437,6 +454,7 @@ func (s *ServerRequiredSuite) TestPostWithoutHash(c *C) {
                req, err := http.NewRequest("POST",
                        "http://"+listener.Addr().String()+"/",
                        strings.NewReader("qux"))
+               c.Check(err, IsNil)
                req.Header.Add("Authorization", "OAuth2 "+arvadostest.ActiveToken)
                req.Header.Add("Content-Type", "application/octet-stream")
                resp, err := client.Do(req)
@@ -471,7 +489,22 @@ func (s *ServerRequiredSuite) TestStripHint(c *C) {
 //   With a valid but non-existing prefix (expect "\n")
 //   With an invalid prefix (expect error)
 func (s *ServerRequiredSuite) TestGetIndex(c *C) {
-       kc := runProxy(c, nil, false)
+       getIndexWorker(c, false)
+}
+
+// Test GetIndex
+//   Uses config.yml
+//   Put one block, with 2 replicas
+//   With no prefix (expect the block locator, twice)
+//   With an existing prefix (expect the block locator, twice)
+//   With a valid but non-existing prefix (expect "\n")
+//   With an invalid prefix (expect error)
+func (s *ServerRequiredConfigYmlSuite) TestGetIndex(c *C) {
+       getIndexWorker(c, true)
+}
+
+func getIndexWorker(c *C, useConfig bool) {
+       kc := runProxy(c, false, useConfig)
        defer closeListener()
 
        // Put "index-data" blocks
@@ -484,16 +517,17 @@ func (s *ServerRequiredSuite) TestGetIndex(c *C) {
        c.Check(err, Equals, nil)
 
        reader, blocklen, _, err := kc.Get(hash)
-       c.Assert(err, Equals, nil)
+       c.Assert(err, IsNil)
        c.Check(blocklen, Equals, int64(10))
        all, err := ioutil.ReadAll(reader)
+       c.Assert(err, IsNil)
        c.Check(all, DeepEquals, data)
 
        // Put some more blocks
        _, _, err = kc.PutB([]byte("some-more-index-data"))
        c.Check(err, IsNil)
 
-       kc.Arvados.ApiToken = arvadostest.DataManagerToken
+       kc.Arvados.ApiToken = arvadostest.SystemRootToken
 
        // Invoke GetIndex
        for _, spec := range []struct {
@@ -533,7 +567,7 @@ func (s *ServerRequiredSuite) TestGetIndex(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
        hash, _, err := kc.PutB([]byte("shareddata"))
        c.Check(err, IsNil)
@@ -546,7 +580,7 @@ func (s *ServerRequiredSuite) TestCollectionSharingToken(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        // Put a test block
@@ -583,34 +617,33 @@ func (s *ServerRequiredSuite) TestPutAskGetInvalidToken(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestAskGetKeepProxyConnectionError(c *C) {
-       arv, err := arvadosclient.MakeArvadosClient()
-       c.Assert(err, Equals, nil)
+       kc := runProxy(c, false, false)
+       defer closeListener()
 
-       // keepclient with no such keep server
-       kc := keepclient.New(arv)
+       // Point keepproxy at a non-existent keepstore
        locals := map[string]string{
                TestProxyUUID: "http://localhost:12345",
        }
-       kc.SetServiceRoots(locals, nil, nil)
+       router.(*proxyHandler).KeepClient.SetServiceRoots(locals, nil, nil)
 
-       // Ask should result in temporary connection refused error
+       // Ask should result in temporary bad gateway error
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
-       _, _, err = kc.Ask(hash)
+       _, _, err := kc.Ask(hash)
        c.Check(err, NotNil)
        errNotFound, _ := err.(*keepclient.ErrNotFound)
        c.Check(errNotFound.Temporary(), Equals, true)
-       c.Assert(err, ErrorMatches, ".*connection refused.*")
+       c.Assert(err, ErrorMatches, ".*HTTP 502.*")
 
-       // Get should result in temporary connection refused error
+       // Get should result in temporary bad gateway error
        _, _, _, err = kc.Get(hash)
        c.Check(err, NotNil)
        errNotFound, _ = err.(*keepclient.ErrNotFound)
        c.Check(errNotFound.Temporary(), Equals, true)
-       c.Assert(err, ErrorMatches, ".*connection refused.*")
+       c.Assert(err, ErrorMatches, ".*HTTP 502.*")
 }
 
 func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
        hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
@@ -633,10 +666,10 @@ func (s *NoKeepServerSuite) TestAskGetNoKeepServerError(c *C) {
 }
 
 func (s *ServerRequiredSuite) TestPing(c *C) {
-       kc := runProxy(c, nil, false)
+       kc := runProxy(c, false, false)
        defer closeListener()
 
-       rtr := MakeRESTRouter(true, true, kc, 10*time.Second, arvadostest.ManagementToken)
+       rtr := MakeRESTRouter(kc, 10*time.Second, arvadostest.ManagementToken)
 
        req, err := http.NewRequest("GET",
                "http://"+listener.Addr().String()+"/_health/ping",