X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/ce4285dd9a6310a799b861237918273329390316..2c6557f613fcf6cdcebb08c321a5d061aeb780c6:/services/keepstore/proxy_remote_test.go diff --git a/services/keepstore/proxy_remote_test.go b/services/keepstore/proxy_remote_test.go index 4c50513808..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)) @@ -120,30 +119,108 @@ func (s *ProxyRemoteSuite) TestProxyRemote(c *check.C) { path := "/" + strings.Replace(s.remoteKeepLocator, "+A", "+R"+s.remoteClusterID+"-", 1) - var req *http.Request - var resp *httptest.ResponseRecorder - tryWithToken := func(token string) { - req = httptest.NewRequest("GET", path, nil) - req.Header.Set("Authorization", "Bearer "+token) + for _, trial := range []struct { + label string + method string + token string + xKeepSignature string + expectRemoteReqs int64 + expectCode int + expectSignature bool + }{ + { + 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) + + s.remoteKeepRequests = 0 + + var req *http.Request + var resp *httptest.ResponseRecorder + 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) + 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 { + 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) } - - // Happy path - tryWithToken(arvadostest.ActiveTokenV2) - c.Check(s.remoteKeepRequests, check.Equals, int64(1)) - c.Check(resp.Code, check.Equals, http.StatusOK) - c.Check(resp.Body.String(), check.Equals, string(data)) - - // Obsolete token - tryWithToken(arvadostest.ActiveToken) - c.Check(s.remoteKeepRequests, check.Equals, int64(1)) - c.Check(resp.Code, check.Equals, http.StatusBadRequest) - c.Check(resp.Body.String(), check.Not(check.Equals), string(data)) - - // Bad token - tryWithToken(arvadostest.ActiveTokenV2[:len(arvadostest.ActiveTokenV2)-3] + "xxx") - c.Check(s.remoteKeepRequests, check.Equals, int64(2)) - c.Check(resp.Code, check.Equals, http.StatusNotFound) - c.Check(resp.Body.String(), check.Not(check.Equals), string(data)) }