X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/a1745a13f68363598cc121b617436f3ac5b1654d..e0dfdad390b88924f15e4f6e7d8e7eadd585e06f:/services/keepstore/proxy_remote_test.go diff --git a/services/keepstore/proxy_remote_test.go b/services/keepstore/proxy_remote_test.go index b15e0b0683..886754e14a 100644 --- a/services/keepstore/proxy_remote_test.go +++ b/services/keepstore/proxy_remote_test.go @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: AGPL-3.0 -package main +package keepstore import ( "crypto/md5" @@ -16,19 +16,21 @@ import ( "sync/atomic" "time" - "git.curoverse.com/arvados.git/sdk/go/arvados" - "git.curoverse.com/arvados.git/sdk/go/arvadostest" - "git.curoverse.com/arvados.git/sdk/go/auth" - "git.curoverse.com/arvados.git/sdk/go/keepclient" + "git.arvados.org/arvados.git/sdk/go/arvados" + "git.arvados.org/arvados.git/sdk/go/arvadostest" + "git.arvados.org/arvados.git/sdk/go/auth" + "git.arvados.org/arvados.git/sdk/go/ctxlog" + "git.arvados.org/arvados.git/sdk/go/httpserver" + "git.arvados.org/arvados.git/sdk/go/keepclient" + "github.com/prometheus/client_golang/prometheus" check "gopkg.in/check.v1" ) -var _ = check.Suite(&ProxyRemoteSuite{}) +var _ = check.Suite(&proxyRemoteSuite{}) -type ProxyRemoteSuite struct { +type proxyRemoteSuite struct { cluster *arvados.Cluster - vm VolumeManager - rtr http.Handler + handler *router remoteClusterID string remoteBlobSigningKey []byte @@ -39,7 +41,7 @@ type ProxyRemoteSuite struct { remoteAPI *httptest.Server } -func (s *ProxyRemoteSuite) remoteKeepproxyHandler(w http.ResponseWriter, r *http.Request) { +func (s *proxyRemoteSuite) remoteKeepproxyHandler(w http.ResponseWriter, r *http.Request) { expectToken, err := auth.SaltToken(arvadostest.ActiveTokenV2, s.remoteClusterID) if err != nil { panic(err) @@ -56,7 +58,7 @@ func (s *ProxyRemoteSuite) remoteKeepproxyHandler(w http.ResponseWriter, r *http http.Error(w, "404", 404) } -func (s *ProxyRemoteSuite) remoteAPIHandler(w http.ResponseWriter, r *http.Request) { +func (s *proxyRemoteSuite) remoteAPIHandler(w http.ResponseWriter, r *http.Request) { host, port, _ := net.SplitHostPort(strings.Split(s.remoteKeepproxy.URL, "//")[1]) portnum, _ := strconv.Atoi(port) if r.URL.Path == "/arvados/v1/discovery/v1/rest" { @@ -80,39 +82,36 @@ func (s *ProxyRemoteSuite) remoteAPIHandler(w http.ResponseWriter, r *http.Reque http.Error(w, "404", 404) } -func (s *ProxyRemoteSuite) SetUpTest(c *check.C) { +func (s *proxyRemoteSuite) SetUpTest(c *check.C) { s.remoteClusterID = "z0000" s.remoteBlobSigningKey = []byte("3b6df6fb6518afe12922a5bc8e67bf180a358bc8") - s.remoteKeepproxy = httptest.NewServer(http.HandlerFunc(s.remoteKeepproxyHandler)) + s.remoteKeepproxy = httptest.NewServer(httpserver.LogRequests(http.HandlerFunc(s.remoteKeepproxyHandler))) s.remoteAPI = httptest.NewUnstartedServer(http.HandlerFunc(s.remoteAPIHandler)) s.remoteAPI.StartTLS() - s.cluster = arvados.IntegrationTestCluster() + s.cluster = testCluster(c) s.cluster.RemoteClusters = map[string]arvados.RemoteCluster{ - s.remoteClusterID: arvados.RemoteCluster{ + s.remoteClusterID: { Host: strings.Split(s.remoteAPI.URL, "//")[1], Proxy: true, Scheme: "http", Insecure: true, }, } - s.vm = MakeTestVolumeManager(2) - KeepVM = s.vm - theConfig = DefaultConfig() - theConfig.systemAuthToken = arvadostest.DataManagerToken - theConfig.Start() - s.rtr = MakeRESTRouter(s.cluster) + s.cluster.Volumes = map[string]arvados.Volume{"zzzzz-nyw5e-000000000000000": {Driver: "stub"}} } -func (s *ProxyRemoteSuite) TearDownTest(c *check.C) { - s.vm.Close() - KeepVM = nil - theConfig = DefaultConfig() - theConfig.Start() +func (s *proxyRemoteSuite) TearDownTest(c *check.C) { s.remoteAPI.Close() s.remoteKeepproxy.Close() } -func (s *ProxyRemoteSuite) TestProxyRemote(c *check.C) { +func (s *proxyRemoteSuite) TestProxyRemote(c *check.C) { + reg := prometheus.NewRegistry() + router, cancel := testRouter(c, s.cluster, reg) + defer cancel() + instrumented := httpserver.Instrument(reg, ctxlog.TestLogger(c), router) + handler := httpserver.LogRequests(instrumented.ServeAPI(s.cluster.ManagementToken, instrumented)) + data := []byte("foo bar") s.remoteKeepData = data locator := fmt.Sprintf("%x+%d", md5.Sum(data), len(data)) @@ -122,45 +121,106 @@ func (s *ProxyRemoteSuite) TestProxyRemote(c *check.C) { for _, trial := range []struct { label string + method string token string + xKeepSignature string expectRemoteReqs int64 expectCode int + expectSignature bool }{ { - label: "happy path", + label: "GET only", + method: "GET", token: arvadostest.ActiveTokenV2, expectRemoteReqs: 1, expectCode: http.StatusOK, }, { label: "obsolete token", + method: "GET", token: arvadostest.ActiveToken, expectRemoteReqs: 0, expectCode: http.StatusBadRequest, }, { label: "bad token", + method: "GET", token: arvadostest.ActiveTokenV2[:len(arvadostest.ActiveTokenV2)-3] + "xxx", expectRemoteReqs: 1, expectCode: http.StatusNotFound, }, + { + label: "HEAD only", + method: "HEAD", + token: arvadostest.ActiveTokenV2, + expectRemoteReqs: 1, + expectCode: http.StatusOK, + }, + { + label: "HEAD with local signature", + method: "HEAD", + xKeepSignature: "local, time=" + time.Now().Format(time.RFC3339), + token: arvadostest.ActiveTokenV2, + expectRemoteReqs: 1, + expectCode: http.StatusOK, + expectSignature: true, + }, + { + label: "GET with local signature", + method: "GET", + xKeepSignature: "local, time=" + time.Now().Format(time.RFC3339), + token: arvadostest.ActiveTokenV2, + expectRemoteReqs: 1, + expectCode: http.StatusOK, + expectSignature: true, + }, } { - c.Logf("trial: %s", trial.label) + c.Logf("=== trial: %s", trial.label) s.remoteKeepRequests = 0 var req *http.Request var resp *httptest.ResponseRecorder - req = httptest.NewRequest("GET", path, nil) + req = httptest.NewRequest(trial.method, path, nil) req.Header.Set("Authorization", "Bearer "+trial.token) + if trial.xKeepSignature != "" { + req.Header.Set("X-Keep-Signature", trial.xKeepSignature) + } resp = httptest.NewRecorder() - s.rtr.ServeHTTP(resp, req) + handler.ServeHTTP(resp, req) c.Check(s.remoteKeepRequests, check.Equals, trial.expectRemoteReqs) - c.Check(resp.Code, check.Equals, trial.expectCode) + if !c.Check(resp.Code, check.Equals, trial.expectCode) { + c.Logf("resp.Code %d came with resp.Body %q", resp.Code, resp.Body.String()) + } if resp.Code == http.StatusOK { - c.Check(resp.Body.String(), check.Equals, string(data)) + if trial.method == "HEAD" { + c.Check(resp.Body.String(), check.Equals, "") + c.Check(resp.Result().ContentLength, check.Equals, int64(len(data))) + } else { + c.Check(resp.Body.String(), check.Equals, string(data)) + } } else { c.Check(resp.Body.String(), check.Not(check.Equals), string(data)) } + + c.Check(resp.Header().Get("Vary"), check.Matches, `(.*, )?X-Keep-Signature(, .*)?`) + + locHdr := resp.Header().Get("X-Keep-Locator") + if !trial.expectSignature { + c.Check(locHdr, check.Equals, "") + continue + } + + c.Check(locHdr, check.Not(check.Equals), "") + c.Check(locHdr, check.Not(check.Matches), `.*\+R.*`) + c.Check(arvados.VerifySignature(locHdr, trial.token, s.cluster.Collections.BlobSigningTTL.Duration(), []byte(s.cluster.Collections.BlobSigningKey)), check.IsNil) + + // Ensure block can be requested using new signature + req = httptest.NewRequest("GET", "/"+locHdr, nil) + req.Header.Set("Authorization", "Bearer "+trial.token) + resp = httptest.NewRecorder() + handler.ServeHTTP(resp, req) + c.Check(resp.Code, check.Equals, http.StatusOK) + c.Check(s.remoteKeepRequests, check.Equals, trial.expectRemoteReqs) } }