22184: Force sync in depth header test sequence.
[arvados.git] / services / keep-web / cache_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package keepweb
6
7 import (
8         "net/http"
9         "net/http/httptest"
10         "regexp"
11         "strings"
12         "time"
13
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15         "git.arvados.org/arvados.git/sdk/go/arvadostest"
16         "gopkg.in/check.v1"
17 )
18
19 func (s *IntegrationSuite) checkCacheMetrics(c *check.C, regs ...string) {
20         s.handler.Cache.updateGauges()
21         mm := arvadostest.GatherMetricsAsString(s.handler.Cache.registry)
22         // Remove comments to make the "value vs. regexp" failure
23         // output easier to read.
24         mm = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(mm, "")
25         for _, reg := range regs {
26                 c.Check(mm, check.Matches, `(?ms).*keepweb_sessions_`+reg+`\n.*`)
27         }
28 }
29
30 func (s *IntegrationSuite) TestCache(c *check.C) {
31         // Hit the same collection 5 times using the same token. Only
32         // the first req should cause an API call; the next 4 should
33         // hit all caches.
34         u := mustParseURL("http://" + arvadostest.FooCollection + ".keep-web.example/foo")
35         req := &http.Request{
36                 Method:     "GET",
37                 Host:       u.Host,
38                 URL:        u,
39                 RequestURI: u.RequestURI(),
40                 Header: http.Header{
41                         "Authorization": {"Bearer " + arvadostest.ActiveToken},
42                 },
43         }
44         for i := 0; i < 5; i++ {
45                 resp := httptest.NewRecorder()
46                 s.handler.ServeHTTP(resp, req)
47                 c.Check(resp.Code, check.Equals, http.StatusOK)
48         }
49         s.checkCacheMetrics(c,
50                 "hits 4",
51                 "misses 1",
52                 "active 1")
53
54         // Hit a shared collection 3 times using PDH, using a
55         // different token.
56         u2 := mustParseURL("http://" + strings.Replace(arvadostest.BarFileCollectionPDH, "+", "-", 1) + ".keep-web.example/bar")
57         req2 := &http.Request{
58                 Method:     "GET",
59                 Host:       u2.Host,
60                 URL:        u2,
61                 RequestURI: u2.RequestURI(),
62                 Header: http.Header{
63                         "Authorization": {"Bearer " + arvadostest.SpectatorToken},
64                 },
65         }
66         for i := 0; i < 3; i++ {
67                 resp2 := httptest.NewRecorder()
68                 s.handler.ServeHTTP(resp2, req2)
69                 c.Check(resp2.Code, check.Equals, http.StatusOK)
70         }
71         s.checkCacheMetrics(c,
72                 "hits 6",
73                 "misses 2",
74                 "active 2")
75
76         // Alternating between two collections/tokens N times should
77         // use the existing sessions.
78         for i := 0; i < 7; i++ {
79                 resp := httptest.NewRecorder()
80                 s.handler.ServeHTTP(resp, req)
81                 c.Check(resp.Code, check.Equals, http.StatusOK)
82
83                 resp2 := httptest.NewRecorder()
84                 s.handler.ServeHTTP(resp2, req2)
85                 c.Check(resp2.Code, check.Equals, http.StatusOK)
86         }
87         s.checkCacheMetrics(c,
88                 "hits 20",
89                 "misses 2",
90                 "active 2")
91 }
92
93 func (s *IntegrationSuite) TestForceReloadPDH(c *check.C) {
94         filename := strings.Replace(time.Now().Format(time.RFC3339Nano), ":", ".", -1)
95         manifest := ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:" + filename + "\n"
96         pdh := arvados.PortableDataHash(manifest)
97         client := arvados.NewClientFromEnv()
98         client.AuthToken = arvadostest.ActiveToken
99
100         _, resp := s.do("GET", "http://"+strings.Replace(pdh, "+", "-", 1)+".keep-web.example/"+filename, arvadostest.ActiveToken, nil)
101         c.Check(resp.Code, check.Equals, http.StatusNotFound)
102
103         var coll arvados.Collection
104         err := client.RequestAndDecode(&coll, "POST", "arvados/v1/collections", nil, map[string]interface{}{
105                 "collection": map[string]string{
106                         "manifest_text": manifest,
107                 },
108         })
109         c.Assert(err, check.IsNil)
110         defer client.RequestAndDecode(nil, "DELETE", "arvados/v1/collections/"+coll.UUID, nil, nil)
111         c.Assert(coll.PortableDataHash, check.Equals, pdh)
112
113         _, resp = s.do("GET", "http://"+strings.Replace(pdh, "+", "-", 1)+".keep-web.example/"+filename, "", http.Header{
114                 "Authorization": {"Bearer " + arvadostest.ActiveToken},
115                 "Cache-Control": {"must-revalidate"},
116         })
117         c.Check(resp.Code, check.Equals, http.StatusOK)
118
119         _, resp = s.do("GET", "http://"+strings.Replace(pdh, "+", "-", 1)+".keep-web.example/missingfile", "", http.Header{
120                 "Authorization": {"Bearer " + arvadostest.ActiveToken},
121                 "Cache-Control": {"must-revalidate"},
122         })
123         c.Check(resp.Code, check.Equals, http.StatusNotFound)
124 }
125
126 func (s *IntegrationSuite) TestForceReloadUUID(c *check.C) {
127         client := arvados.NewClientFromEnv()
128         client.AuthToken = arvadostest.ActiveToken
129         var coll arvados.Collection
130         err := client.RequestAndDecode(&coll, "POST", "arvados/v1/collections", nil, map[string]interface{}{
131                 "collection": map[string]string{
132                         "manifest_text": ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:empty_file\n",
133                 },
134         })
135         c.Assert(err, check.IsNil)
136         defer client.RequestAndDecode(nil, "DELETE", "arvados/v1/collections/"+coll.UUID, nil, nil)
137
138         _, resp := s.do("GET", "http://"+coll.UUID+".keep-web.example/different_empty_file", arvadostest.ActiveToken, nil)
139         c.Check(resp.Code, check.Equals, http.StatusNotFound)
140         _, resp = s.do("GET", "http://"+coll.UUID+".keep-web.example/empty_file", arvadostest.ActiveToken, nil)
141         c.Check(resp.Code, check.Equals, http.StatusOK)
142         _, resp = s.do("GET", "http://"+coll.UUID+".keep-web.example/different_empty_file", arvadostest.ActiveToken, nil)
143         c.Check(resp.Code, check.Equals, http.StatusNotFound)
144         err = client.RequestAndDecode(&coll, "PATCH", "arvados/v1/collections/"+coll.UUID, nil, map[string]interface{}{
145                 "collection": map[string]string{
146                         "manifest_text": ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:different_empty_file\n",
147                 },
148         })
149         c.Assert(err, check.IsNil)
150         // Before we set the force-reload header, the cached version
151         // with empty_file is still accessible.
152         _, resp = s.do("GET", "http://"+coll.UUID+".keep-web.example/empty_file", arvadostest.ActiveToken, nil)
153         c.Check(resp.Code, check.Equals, http.StatusOK)
154         // If we set the force-reload header, we get the latest
155         // version and empty_file is gone.
156         _, resp = s.do("GET", "http://"+coll.UUID+".keep-web.example/empty_file", "", http.Header{
157                 "Authorization": {"Bearer " + arvadostest.ActiveToken},
158                 "Cache-Control": {"must-revalidate"},
159         })
160         c.Check(resp.Code, check.Equals, http.StatusNotFound)
161         _, resp = s.do("GET", "http://"+coll.UUID+".keep-web.example/different_empty_file", arvadostest.ActiveToken, nil)
162         c.Check(resp.Code, check.Equals, http.StatusOK)
163 }