1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
23 "git.arvados.org/arvados.git/sdk/go/arvados"
24 "git.arvados.org/arvados.git/sdk/go/arvadosclient"
25 "git.arvados.org/arvados.git/sdk/go/arvadostest"
29 func Test(t *testing.T) {
30 DefaultRetryDelay = 50 * time.Millisecond
34 // Gocheck boilerplate
35 var _ = Suite(&ServerRequiredSuite{})
36 var _ = Suite(&StandaloneSuite{})
38 // Tests that require the Keep server running
39 type ServerRequiredSuite struct{}
42 type StandaloneSuite struct {
43 origDefaultRetryDelay time.Duration
44 origMinimumRetryDelay time.Duration
47 var origHOME = os.Getenv("HOME")
49 func (s *StandaloneSuite) SetUpTest(c *C) {
50 RefreshServiceDiscovery()
51 // Prevent cache state from leaking between test cases
52 os.Setenv("HOME", c.MkDir())
53 s.origDefaultRetryDelay = DefaultRetryDelay
54 s.origMinimumRetryDelay = MinimumRetryDelay
57 func (s *StandaloneSuite) TearDownTest(c *C) {
58 os.Setenv("HOME", origHOME)
59 DefaultRetryDelay = s.origDefaultRetryDelay
60 MinimumRetryDelay = s.origMinimumRetryDelay
63 func pythonDir() string {
65 return fmt.Sprintf("%s/../../python/tests", cwd)
68 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
69 arvadostest.StartKeep(2, false)
72 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
73 arvadostest.StopKeep(2)
74 os.Setenv("HOME", origHOME)
77 func (s *ServerRequiredSuite) SetUpTest(c *C) {
78 RefreshServiceDiscovery()
79 // Prevent cache state from leaking between test cases
80 os.Setenv("HOME", c.MkDir())
83 func (s *ServerRequiredSuite) TestMakeKeepClient(c *C) {
84 arv, err := arvadosclient.MakeArvadosClient()
87 kc, err := MakeKeepClient(arv)
90 c.Check(len(kc.LocalRoots()), Equals, 2)
91 for _, root := range kc.LocalRoots() {
92 c.Check(root, Matches, "http://localhost:\\d+")
96 func (s *ServerRequiredSuite) TestDefaultStorageClasses(c *C) {
97 arv, err := arvadosclient.MakeArvadosClient()
100 cc, err := arv.ClusterConfig("StorageClasses")
103 c.Assert(cc.(map[string]interface{})["default"], NotNil)
106 c.Assert(kc.DefaultStorageClasses, DeepEquals, []string{"default"})
109 func (s *ServerRequiredSuite) TestDefaultReplications(c *C) {
110 arv, err := arvadosclient.MakeArvadosClient()
113 kc, err := MakeKeepClient(arv)
115 c.Assert(kc.Want_replicas, Equals, 2)
117 arv.DiscoveryDoc["defaultCollectionReplication"] = 3.0
118 kc, err = MakeKeepClient(arv)
120 c.Assert(kc.Want_replicas, Equals, 3)
122 arv.DiscoveryDoc["defaultCollectionReplication"] = 1.0
123 kc, err = MakeKeepClient(arv)
125 c.Assert(kc.Want_replicas, Equals, 1)
128 type StubPutHandler struct {
131 expectAPIToken string
133 expectStorageClass string
134 returnStorageClasses string
136 requests []*http.Request
140 func (sph *StubPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
142 sph.requests = append(sph.requests, req)
144 sph.c.Check(req.URL.Path, Equals, "/"+sph.expectPath)
145 sph.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("Bearer %s", sph.expectAPIToken))
146 if sph.expectStorageClass != "*" {
147 sph.c.Check(req.Header.Get("X-Keep-Storage-Classes"), Equals, sph.expectStorageClass)
149 body, err := ioutil.ReadAll(req.Body)
150 sph.c.Check(err, IsNil)
151 sph.c.Check(body, DeepEquals, []byte(sph.expectBody))
152 resp.Header().Set("X-Keep-Replicas-Stored", "1")
153 if sph.returnStorageClasses != "" {
154 resp.Header().Set("X-Keep-Storage-Classes-Confirmed", sph.returnStorageClasses)
156 resp.WriteHeader(200)
157 sph.handled <- fmt.Sprintf("http://%s", req.Host)
160 func RunFakeKeepServer(st http.Handler) (ks KeepServer) {
162 // If we don't explicitly bind it to localhost, ks.listener.Addr() will
163 // bind to 0.0.0.0 or [::] which is not a valid address for Dial()
164 ks.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0})
166 panic("Could not listen on any port")
168 ks.url = fmt.Sprintf("http://%s", ks.listener.Addr().String())
169 go http.Serve(ks.listener, st)
173 func UploadToStubHelper(c *C, st http.Handler, f func(*KeepClient, string,
174 io.ReadCloser, io.WriteCloser, chan uploadStatus)) {
176 ks := RunFakeKeepServer(st)
177 defer ks.listener.Close()
179 arv, _ := arvadosclient.MakeArvadosClient()
180 arv.ApiToken = "abc123"
182 kc, _ := MakeKeepClient(arv)
184 reader, writer := io.Pipe()
185 uploadStatusChan := make(chan uploadStatus)
187 f(kc, ks.url, reader, writer, uploadStatusChan)
190 func (s *StandaloneSuite) TestUploadToStubKeepServer(c *C) {
191 st := &StubPutHandler{
193 expectPath: "acbd18db4cc2f85cedef654fccc4a4d8",
194 expectAPIToken: "abc123",
196 expectStorageClass: "",
197 returnStorageClasses: "default=1",
198 handled: make(chan string),
201 UploadToStubHelper(c, st,
202 func(kc *KeepClient, url string, reader io.ReadCloser, writer io.WriteCloser, uploadStatusChan chan uploadStatus) {
203 go kc.uploadToKeepServer(url, st.expectPath, nil, reader, uploadStatusChan, len("foo"), kc.getRequestID())
205 writer.Write([]byte("foo"))
209 status := <-uploadStatusChan
210 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, map[string]int{"default": 1}, ""})
214 func (s *StandaloneSuite) TestUploadToStubKeepServerBufferReader(c *C) {
215 st := &StubPutHandler{
217 expectPath: "acbd18db4cc2f85cedef654fccc4a4d8",
218 expectAPIToken: "abc123",
220 expectStorageClass: "",
221 returnStorageClasses: "default=1",
222 handled: make(chan string),
225 UploadToStubHelper(c, st,
226 func(kc *KeepClient, url string, _ io.ReadCloser, _ io.WriteCloser, uploadStatusChan chan uploadStatus) {
227 go kc.uploadToKeepServer(url, st.expectPath, nil, bytes.NewBuffer([]byte("foo")), uploadStatusChan, 3, kc.getRequestID())
231 status := <-uploadStatusChan
232 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, map[string]int{"default": 1}, ""})
236 func (s *StandaloneSuite) TestUploadWithStorageClasses(c *C) {
237 for _, trial := range []struct {
239 expectMap map[string]int
242 {"foo=1", map[string]int{"foo": 1}},
243 {" foo=1 , bar=2 ", map[string]int{"foo": 1, "bar": 2}},
247 st := &StubPutHandler{
249 expectPath: "acbd18db4cc2f85cedef654fccc4a4d8",
250 expectAPIToken: "abc123",
252 expectStorageClass: "",
253 returnStorageClasses: trial.respHeader,
254 handled: make(chan string),
257 UploadToStubHelper(c, st,
258 func(kc *KeepClient, url string, reader io.ReadCloser, writer io.WriteCloser, uploadStatusChan chan uploadStatus) {
259 go kc.uploadToKeepServer(url, st.expectPath, nil, reader, uploadStatusChan, len("foo"), kc.getRequestID())
261 writer.Write([]byte("foo"))
265 status := <-uploadStatusChan
266 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, trial.expectMap, ""})
271 func (s *StandaloneSuite) TestPutWithoutStorageClassesClusterSupport(c *C) {
273 for _, trial := range []struct {
275 clientClasses []string
281 // Talking to an older cluster (no default storage classes exported
282 // config) and no other additional storage classes requirements.
283 {1, nil, nil, 1, 1, true},
284 {2, nil, nil, 2, 2, true},
285 {3, nil, nil, 3, 3, true},
286 {nServers*2 + 1, nil, nil, nServers, nServers, false},
288 {1, []string{"class1"}, nil, 1, 1, true},
289 {2, []string{"class1"}, nil, 2, 2, true},
290 {3, []string{"class1"}, nil, 3, 3, true},
291 {1, []string{"class1", "class2"}, nil, 1, 1, true},
292 {nServers*2 + 1, []string{"class1"}, nil, nServers, nServers, false},
294 {1, nil, []string{"class1"}, 1, 1, true},
295 {2, nil, []string{"class1"}, 2, 2, true},
296 {3, nil, []string{"class1"}, 3, 3, true},
297 {1, nil, []string{"class1", "class2"}, 1, 1, true},
298 {nServers*2 + 1, nil, []string{"class1"}, nServers, nServers, false},
301 st := &StubPutHandler{
303 expectPath: "acbd18db4cc2f85cedef654fccc4a4d8",
304 expectAPIToken: "abc123",
306 expectStorageClass: "*",
307 returnStorageClasses: "", // Simulate old cluster without SC keep support
308 handled: make(chan string, 100),
310 ks := RunSomeFakeKeepServers(st, nServers)
311 arv, _ := arvadosclient.MakeArvadosClient()
312 kc, _ := MakeKeepClient(arv)
313 kc.Want_replicas = trial.replicas
314 kc.StorageClasses = trial.clientClasses
315 kc.DefaultStorageClasses = nil // Simulate an old cluster without SC defaults
316 arv.ApiToken = "abc123"
317 localRoots := make(map[string]string)
318 writableLocalRoots := make(map[string]string)
319 for i, k := range ks {
320 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
321 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
322 defer k.listener.Close()
324 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
326 _, err := kc.BlockWrite(context.Background(), arvados.BlockWriteOptions{
328 StorageClasses: trial.putClasses,
335 c.Check(len(st.handled) >= trial.minRequests, Equals, true, Commentf("len(st.handled)==%d, trial.minRequests==%d", len(st.handled), trial.minRequests))
336 c.Check(len(st.handled) <= trial.maxRequests, Equals, true, Commentf("len(st.handled)==%d, trial.maxRequests==%d", len(st.handled), trial.maxRequests))
337 if trial.clientClasses == nil && trial.putClasses == nil {
338 c.Check(st.requests[0].Header.Get("X-Keep-Storage-Classes"), Equals, "")
343 func (s *StandaloneSuite) TestPutWithStorageClasses(c *C) {
345 for _, trial := range []struct {
347 defaultClasses []string
348 clientClasses []string // clientClasses takes precedence over defaultClasses
349 putClasses []string // putClasses takes precedence over clientClasses
354 {1, []string{"class1"}, nil, nil, 1, 1, true},
355 {2, []string{"class1"}, nil, nil, 1, 2, true},
356 {3, []string{"class1"}, nil, nil, 2, 3, true},
357 {1, []string{"class1", "class2"}, nil, nil, 1, 1, true},
359 // defaultClasses doesn't matter when any of the others is specified.
360 {1, []string{"class1"}, []string{"class1"}, nil, 1, 1, true},
361 {2, []string{"class1"}, []string{"class1"}, nil, 1, 2, true},
362 {3, []string{"class1"}, []string{"class1"}, nil, 2, 3, true},
363 {1, []string{"class1"}, []string{"class1", "class2"}, nil, 1, 1, true},
364 {3, []string{"class1"}, nil, []string{"class1"}, 2, 3, true},
365 {1, []string{"class1"}, nil, []string{"class1", "class2"}, 1, 1, true},
366 {1, []string{"class1"}, []string{"class404"}, []string{"class1", "class2"}, 1, 1, true},
367 {1, []string{"class1"}, []string{"class1"}, []string{"class404", "class2"}, nServers, nServers, false},
368 {nServers*2 + 1, []string{}, []string{"class1"}, nil, nServers, nServers, false},
369 {1, []string{"class1"}, []string{"class404"}, nil, nServers, nServers, false},
370 {1, []string{"class1"}, []string{"class1", "class404"}, nil, nServers, nServers, false},
371 {1, []string{"class1"}, nil, []string{"class1", "class404"}, nServers, nServers, false},
374 st := &StubPutHandler{
376 expectPath: "acbd18db4cc2f85cedef654fccc4a4d8",
377 expectAPIToken: "abc123",
379 expectStorageClass: "*",
380 returnStorageClasses: "class1=2, class2=2",
381 handled: make(chan string, 100),
383 ks := RunSomeFakeKeepServers(st, nServers)
384 arv, _ := arvadosclient.MakeArvadosClient()
385 kc, _ := MakeKeepClient(arv)
386 kc.Want_replicas = trial.replicas
387 kc.StorageClasses = trial.clientClasses
388 kc.DefaultStorageClasses = trial.defaultClasses
389 arv.ApiToken = "abc123"
390 localRoots := make(map[string]string)
391 writableLocalRoots := make(map[string]string)
392 for i, k := range ks {
393 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
394 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
395 defer k.listener.Close()
397 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
399 _, err := kc.BlockWrite(context.Background(), arvados.BlockWriteOptions{
401 StorageClasses: trial.putClasses,
408 c.Check(len(st.handled) >= trial.minRequests, Equals, true, Commentf("len(st.handled)==%d, trial.minRequests==%d", len(st.handled), trial.minRequests))
409 c.Check(len(st.handled) <= trial.maxRequests, Equals, true, Commentf("len(st.handled)==%d, trial.maxRequests==%d", len(st.handled), trial.maxRequests))
410 if !trial.success && trial.replicas == 1 && c.Check(len(st.requests) >= 2, Equals, true) {
411 // Max concurrency should be 1. First request
412 // should have succeeded for class1. Second
413 // request should only ask for class404.
414 c.Check(st.requests[1].Header.Get("X-Keep-Storage-Classes"), Equals, "class404")
419 type FailHandler struct {
423 func (fh FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
424 resp.WriteHeader(500)
425 fh.handled <- fmt.Sprintf("http://%s", req.Host)
428 type FailThenSucceedHandler struct {
429 morefails int // fail 1 + this many times before succeeding
432 successhandler http.Handler
436 func (fh *FailThenSucceedHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
437 fh.reqIDs = append(fh.reqIDs, req.Header.Get("X-Request-Id"))
438 if int(fh.count.Add(1)) <= fh.morefails+1 {
439 resp.WriteHeader(500)
440 fh.handled <- fmt.Sprintf("http://%s", req.Host)
442 fh.successhandler.ServeHTTP(resp, req)
446 type Error404Handler struct {
450 func (fh Error404Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
451 resp.WriteHeader(404)
452 fh.handled <- fmt.Sprintf("http://%s", req.Host)
455 func (s *StandaloneSuite) TestFailedUploadToStubKeepServer(c *C) {
459 hash := "acbd18db4cc2f85cedef654fccc4a4d8"
461 UploadToStubHelper(c, st,
462 func(kc *KeepClient, url string, reader io.ReadCloser,
463 writer io.WriteCloser, uploadStatusChan chan uploadStatus) {
465 go kc.uploadToKeepServer(url, hash, nil, reader, uploadStatusChan, 3, kc.getRequestID())
467 writer.Write([]byte("foo"))
472 status := <-uploadStatusChan
473 c.Check(status.url, Equals, fmt.Sprintf("%s/%s", url, hash))
474 c.Check(status.statusCode, Equals, 500)
478 type KeepServer struct {
479 listener net.Listener
483 func RunSomeFakeKeepServers(st http.Handler, n int) (ks []KeepServer) {
484 ks = make([]KeepServer, n)
486 for i := 0; i < n; i++ {
487 ks[i] = RunFakeKeepServer(st)
493 func (s *StandaloneSuite) TestPutB(c *C) {
494 hash := Md5String("foo")
496 st := &StubPutHandler{
499 expectAPIToken: "abc123",
501 expectStorageClass: "default",
502 returnStorageClasses: "",
503 handled: make(chan string, 5),
506 arv, _ := arvadosclient.MakeArvadosClient()
507 kc, _ := MakeKeepClient(arv)
510 arv.ApiToken = "abc123"
511 localRoots := make(map[string]string)
512 writableLocalRoots := make(map[string]string)
514 ks := RunSomeFakeKeepServers(st, 5)
516 for i, k := range ks {
517 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
518 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
519 defer k.listener.Close()
522 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
524 kc.PutB([]byte("foo"))
526 shuff := NewRootSorter(
527 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
531 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
532 (s1 == shuff[1] && s2 == shuff[0]),
537 func (s *StandaloneSuite) TestPutHR(c *C) {
538 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
540 st := &StubPutHandler{
543 expectAPIToken: "abc123",
545 expectStorageClass: "default",
546 returnStorageClasses: "",
547 handled: make(chan string, 5),
550 arv, _ := arvadosclient.MakeArvadosClient()
551 kc, _ := MakeKeepClient(arv)
554 arv.ApiToken = "abc123"
555 localRoots := make(map[string]string)
556 writableLocalRoots := make(map[string]string)
558 ks := RunSomeFakeKeepServers(st, 5)
560 for i, k := range ks {
561 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
562 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
563 defer k.listener.Close()
566 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
568 kc.PutHR(hash, bytes.NewBuffer([]byte("foo")), 3)
570 shuff := NewRootSorter(kc.LocalRoots(), hash).GetSortedRoots()
575 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
576 (s1 == shuff[1] && s2 == shuff[0]),
581 func (s *StandaloneSuite) TestPutWithFail(c *C) {
582 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
584 st := &StubPutHandler{
587 expectAPIToken: "abc123",
589 expectStorageClass: "default",
590 returnStorageClasses: "",
591 handled: make(chan string, 4),
595 make(chan string, 1)}
597 arv, err := arvadosclient.MakeArvadosClient()
599 kc, _ := MakeKeepClient(arv)
602 arv.ApiToken = "abc123"
603 localRoots := make(map[string]string)
604 writableLocalRoots := make(map[string]string)
606 ks1 := RunSomeFakeKeepServers(st, 4)
607 ks2 := RunSomeFakeKeepServers(fh, 1)
609 for i, k := range ks1 {
610 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
611 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
612 defer k.listener.Close()
614 for i, k := range ks2 {
615 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
616 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
617 defer k.listener.Close()
620 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
622 shuff := NewRootSorter(
623 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
626 phash, replicas, err := kc.PutB([]byte("foo"))
631 c.Check(phash, Equals, "")
632 c.Check(replicas, Equals, 2)
637 c.Check((s1 == shuff[1] && s2 == shuff[2]) ||
638 (s1 == shuff[2] && s2 == shuff[1]),
643 func (s *StandaloneSuite) TestPutWithTooManyFail(c *C) {
644 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
646 st := &StubPutHandler{
649 expectAPIToken: "abc123",
651 expectStorageClass: "default",
652 returnStorageClasses: "",
653 handled: make(chan string, 1),
657 make(chan string, 4)}
659 arv, err := arvadosclient.MakeArvadosClient()
661 kc, _ := MakeKeepClient(arv)
665 arv.ApiToken = "abc123"
666 localRoots := make(map[string]string)
667 writableLocalRoots := make(map[string]string)
669 ks1 := RunSomeFakeKeepServers(st, 1)
670 ks2 := RunSomeFakeKeepServers(fh, 4)
672 for i, k := range ks1 {
673 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
674 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
675 defer k.listener.Close()
677 for i, k := range ks2 {
678 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
679 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
680 defer k.listener.Close()
683 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
685 _, replicas, err := kc.PutB([]byte("foo"))
687 c.Check(err, FitsTypeOf, InsufficientReplicasError{})
688 c.Check(replicas, Equals, 1)
689 c.Check(<-st.handled, Equals, ks1[0].url)
692 type StubGetHandler struct {
695 expectAPIToken string
700 func (sgh StubGetHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
701 sgh.c.Check(req.URL.Path, Equals, "/"+sgh.expectPath)
702 sgh.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("Bearer %s", sgh.expectAPIToken))
703 resp.WriteHeader(sgh.httpStatus)
704 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(sgh.body)))
708 func (s *StandaloneSuite) TestGet(c *C) {
709 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
711 st := StubGetHandler{
718 ks := RunFakeKeepServer(st)
719 defer ks.listener.Close()
721 arv, err := arvadosclient.MakeArvadosClient()
723 kc, _ := MakeKeepClient(arv)
724 arv.ApiToken = "abc123"
725 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
727 r, n, _, err := kc.Get(hash)
729 c.Check(n, Equals, int64(3))
731 content, err2 := ioutil.ReadAll(r)
733 c.Check(content, DeepEquals, []byte("foo"))
734 c.Check(r.Close(), IsNil)
737 func (s *StandaloneSuite) TestGet404(c *C) {
738 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
740 st := Error404Handler{make(chan string, 1)}
742 ks := RunFakeKeepServer(st)
743 defer ks.listener.Close()
745 arv, err := arvadosclient.MakeArvadosClient()
747 kc, _ := MakeKeepClient(arv)
748 arv.ApiToken = "abc123"
749 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
751 r, n, _, err := kc.Get(hash)
752 c.Check(err, Equals, BlockNotFound)
753 c.Check(n, Equals, int64(0))
757 func (s *StandaloneSuite) TestGetEmptyBlock(c *C) {
758 st := Error404Handler{make(chan string, 1)}
760 ks := RunFakeKeepServer(st)
761 defer ks.listener.Close()
763 arv, err := arvadosclient.MakeArvadosClient()
765 kc, _ := MakeKeepClient(arv)
766 arv.ApiToken = "abc123"
767 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
769 r, n, _, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e+0")
771 c.Check(n, Equals, int64(0))
773 buf, err := ioutil.ReadAll(r)
775 c.Check(buf, DeepEquals, []byte{})
776 c.Check(r.Close(), IsNil)
779 func (s *StandaloneSuite) TestGetFail(c *C) {
780 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
782 st := FailHandler{make(chan string, 1)}
784 ks := RunFakeKeepServer(st)
785 defer ks.listener.Close()
787 arv, err := arvadosclient.MakeArvadosClient()
789 kc, _ := MakeKeepClient(arv)
790 arv.ApiToken = "abc123"
791 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
794 r, n, _, err := kc.Get(hash)
795 errNotFound, _ := err.(*ErrNotFound)
796 if c.Check(errNotFound, NotNil) {
797 c.Check(strings.Contains(errNotFound.Error(), "HTTP 500"), Equals, true)
798 c.Check(errNotFound.Temporary(), Equals, true)
800 c.Check(n, Equals, int64(0))
804 func (s *StandaloneSuite) TestGetFailRetry(c *C) {
805 defer func(origDefault, origMinimum time.Duration) {
806 DefaultRetryDelay = origDefault
807 MinimumRetryDelay = origMinimum
808 }(DefaultRetryDelay, MinimumRetryDelay)
809 DefaultRetryDelay = time.Second / 8
810 MinimumRetryDelay = time.Millisecond
812 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
814 for _, delay := range []time.Duration{0, time.Nanosecond, time.Second / 8, time.Second / 16} {
815 c.Logf("=== initial delay %v", delay)
817 st := &FailThenSucceedHandler{
819 handled: make(chan string, 4),
820 successhandler: StubGetHandler{
827 ks := RunFakeKeepServer(st)
828 defer ks.listener.Close()
830 arv, err := arvadosclient.MakeArvadosClient()
832 kc, _ := MakeKeepClient(arv)
833 arv.ApiToken = "abc123"
834 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
836 kc.RetryDelay = delay
837 kc.DiskCacheSize = DiskCacheDisabled
840 r, n, _, err := kc.Get(hash)
842 c.Check(n, Equals, int64(3))
843 elapsed := time.Since(t0)
845 nonsleeptime := time.Second / 10
846 expect := kc.RetryDelay
848 expect = DefaultRetryDelay
850 min := MinimumRetryDelay * 3
851 max := expect + expect*2 + expect*2*2 + nonsleeptime
852 c.Check(elapsed >= min, Equals, true, Commentf("elapsed %v / expect min %v", elapsed, min))
853 c.Check(elapsed <= max, Equals, true, Commentf("elapsed %v / expect max %v", elapsed, max))
855 content, err := ioutil.ReadAll(r)
857 c.Check(content, DeepEquals, []byte("foo"))
858 c.Check(r.Close(), IsNil)
860 c.Logf("%q", st.reqIDs)
861 if c.Check(st.reqIDs, Not(HasLen), 0) {
862 for _, reqid := range st.reqIDs {
863 c.Check(reqid, Not(Equals), "")
864 c.Check(reqid, Equals, st.reqIDs[0])
870 func (s *StandaloneSuite) TestGetNetError(c *C) {
871 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
873 arv, err := arvadosclient.MakeArvadosClient()
875 kc, _ := MakeKeepClient(arv)
876 arv.ApiToken = "abc123"
877 kc.SetServiceRoots(map[string]string{"x": "http://localhost:62222"}, nil, nil)
879 r, n, _, err := kc.Get(hash)
880 errNotFound, _ := err.(*ErrNotFound)
881 if c.Check(errNotFound, NotNil) {
882 c.Check(strings.Contains(errNotFound.Error(), "connection refused"), Equals, true)
883 c.Check(errNotFound.Temporary(), Equals, true)
885 c.Check(n, Equals, int64(0))
889 func (s *StandaloneSuite) TestGetWithServiceHint(c *C) {
890 uuid := "zzzzz-bi6l4-123451234512345"
891 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
893 // This one shouldn't be used:
894 ks0 := RunFakeKeepServer(StubGetHandler{
900 defer ks0.listener.Close()
901 // This one should be used:
902 ks := RunFakeKeepServer(StubGetHandler{
908 defer ks.listener.Close()
910 arv, err := arvadosclient.MakeArvadosClient()
912 kc, _ := MakeKeepClient(arv)
913 arv.ApiToken = "abc123"
915 map[string]string{"x": ks0.url},
917 map[string]string{uuid: ks.url})
919 r, n, _, err := kc.Get(hash + "+K@" + uuid)
921 c.Check(n, Equals, int64(3))
923 content, err := ioutil.ReadAll(r)
925 c.Check(content, DeepEquals, []byte("foo"))
926 c.Check(r.Close(), IsNil)
929 // Use a service hint to fetch from a local disk service, overriding
930 // rendezvous probe order.
931 func (s *StandaloneSuite) TestGetWithLocalServiceHint(c *C) {
932 uuid := "zzzzz-bi6l4-zzzzzzzzzzzzzzz"
933 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
935 // This one shouldn't be used, although it appears first in
936 // rendezvous probe order:
937 ks0 := RunFakeKeepServer(StubGetHandler{
941 http.StatusBadGateway,
943 defer ks0.listener.Close()
944 // This one should be used:
945 ks := RunFakeKeepServer(StubGetHandler{
951 defer ks.listener.Close()
953 arv, err := arvadosclient.MakeArvadosClient()
955 kc, _ := MakeKeepClient(arv)
956 arv.ApiToken = "abc123"
959 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
960 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
961 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
965 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
966 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
967 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
971 r, n, _, err := kc.Get(hash + "+K@" + uuid)
973 c.Check(n, Equals, int64(3))
975 content, err := ioutil.ReadAll(r)
977 c.Check(content, DeepEquals, []byte("foo"))
978 c.Check(r.Close(), IsNil)
981 func (s *StandaloneSuite) TestGetWithServiceHintFailoverToLocals(c *C) {
982 uuid := "zzzzz-bi6l4-123451234512345"
983 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
985 ksLocal := RunFakeKeepServer(StubGetHandler{
991 defer ksLocal.listener.Close()
992 ksGateway := RunFakeKeepServer(StubGetHandler{
996 http.StatusInternalServerError,
998 defer ksGateway.listener.Close()
1000 arv, err := arvadosclient.MakeArvadosClient()
1002 kc, _ := MakeKeepClient(arv)
1003 arv.ApiToken = "abc123"
1005 map[string]string{"zzzzz-bi6l4-keepdisk0000000": ksLocal.url},
1007 map[string]string{uuid: ksGateway.url})
1009 r, n, _, err := kc.Get(hash + "+K@" + uuid)
1010 c.Assert(err, IsNil)
1011 c.Check(n, Equals, int64(3))
1013 content, err := ioutil.ReadAll(r)
1015 c.Check(content, DeepEquals, []byte("foo"))
1016 c.Check(r.Close(), IsNil)
1019 type BarHandler struct {
1023 func (h BarHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1024 resp.Write([]byte("bar"))
1025 h.handled <- fmt.Sprintf("http://%s", req.Host)
1028 func (s *StandaloneSuite) TestChecksum(c *C) {
1029 foohash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
1030 barhash := fmt.Sprintf("%x+3", md5.Sum([]byte("bar")))
1032 st := BarHandler{make(chan string, 1)}
1034 ks := RunFakeKeepServer(st)
1035 defer ks.listener.Close()
1037 arv, err := arvadosclient.MakeArvadosClient()
1039 kc, _ := MakeKeepClient(arv)
1040 arv.ApiToken = "abc123"
1041 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1043 r, n, _, err := kc.Get(barhash)
1044 if c.Check(err, IsNil) {
1045 _, err = ioutil.ReadAll(r)
1046 c.Check(n, Equals, int64(3))
1052 case <-time.After(time.Second):
1053 c.Fatal("timed out")
1056 r, n, _, err = kc.Get(foohash)
1058 buf, readerr := ioutil.ReadAll(r)
1062 c.Check(err, Equals, BadChecksum)
1066 case <-time.After(time.Second):
1067 c.Fatal("timed out")
1071 func (s *StandaloneSuite) TestGetWithFailures(c *C) {
1072 content := []byte("waz")
1073 hash := fmt.Sprintf("%x+3", md5.Sum(content))
1075 fh := Error404Handler{
1076 make(chan string, 4)}
1078 st := StubGetHandler{
1085 arv, err := arvadosclient.MakeArvadosClient()
1087 kc, _ := MakeKeepClient(arv)
1088 arv.ApiToken = "abc123"
1089 localRoots := make(map[string]string)
1090 writableLocalRoots := make(map[string]string)
1092 ks1 := RunSomeFakeKeepServers(st, 1)
1093 ks2 := RunSomeFakeKeepServers(fh, 4)
1095 for i, k := range ks1 {
1096 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1097 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1098 defer k.listener.Close()
1100 for i, k := range ks2 {
1101 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
1102 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
1103 defer k.listener.Close()
1106 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1109 // This test works only if one of the failing services is
1110 // attempted before the succeeding service. Otherwise,
1111 // <-fh.handled below will just hang! (Probe order depends on
1112 // the choice of block content "waz" and the UUIDs of the fake
1113 // servers, so we just tried different strings until we found
1114 // an example that passes this Assert.)
1115 c.Assert(NewRootSorter(localRoots, hash).GetSortedRoots()[0], Not(Equals), ks1[0].url)
1117 r, n, _, err := kc.Get(hash)
1121 case <-time.After(time.Second):
1122 c.Fatal("timed out")
1124 c.Assert(err, IsNil)
1125 c.Check(n, Equals, int64(3))
1127 readContent, err2 := ioutil.ReadAll(r)
1128 c.Check(err2, IsNil)
1129 c.Check(readContent, DeepEquals, content)
1130 c.Check(r.Close(), IsNil)
1133 func (s *ServerRequiredSuite) TestPutGetHead(c *C) {
1134 content := []byte("TestPutGetHead")
1136 arv, err := arvadosclient.MakeArvadosClient()
1138 kc, err := MakeKeepClient(arv)
1139 c.Assert(err, IsNil)
1141 hash := fmt.Sprintf("%x+%d", md5.Sum(content), len(content))
1144 n, _, err := kc.Ask(hash)
1145 c.Check(err, Equals, BlockNotFound)
1146 c.Check(n, Equals, int64(0))
1149 hash2, replicas, err := kc.PutB(content)
1151 c.Check(hash2, Matches, `\Q`+hash+`\E\b.*`)
1152 c.Check(replicas, Equals, 2)
1155 r, n, _, err := kc.Get(hash)
1157 c.Check(n, Equals, int64(len(content)))
1158 if c.Check(r, NotNil) {
1159 readContent, err := ioutil.ReadAll(r)
1161 if c.Check(len(readContent), Equals, len(content)) {
1162 c.Check(readContent, DeepEquals, content)
1164 c.Check(r.Close(), IsNil)
1168 n, url2, err := kc.Ask(hash)
1170 c.Check(n, Equals, int64(len(content)))
1171 c.Check(url2, Matches, "http://localhost:\\d+/\\Q"+hash+"\\E")
1174 loc, err := kc.LocalLocator(hash)
1176 c.Assert(len(loc) >= 32, Equals, true)
1177 c.Check(loc[:32], Equals, hash[:32])
1180 content := []byte("the perth county conspiracy")
1181 loc, err := kc.LocalLocator(fmt.Sprintf("%x+%d+Rzaaaa-abcde@12345", md5.Sum(content), len(content)))
1182 c.Check(loc, Equals, "")
1183 c.Check(err, ErrorMatches, `.*HEAD .*\+R.*`)
1184 c.Check(err, ErrorMatches, `.*HTTP 400.*`)
1188 type StubProxyHandler struct {
1192 func (h StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1193 resp.Header().Set("X-Keep-Replicas-Stored", "2")
1194 h.handled <- fmt.Sprintf("http://%s", req.Host)
1197 func (s *StandaloneSuite) TestPutProxy(c *C) {
1198 st := StubProxyHandler{make(chan string, 1)}
1200 arv, err := arvadosclient.MakeArvadosClient()
1202 kc, _ := MakeKeepClient(arv)
1204 kc.Want_replicas = 2
1205 arv.ApiToken = "abc123"
1206 localRoots := make(map[string]string)
1207 writableLocalRoots := make(map[string]string)
1209 ks1 := RunSomeFakeKeepServers(st, 1)
1211 for i, k := range ks1 {
1212 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1213 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1214 defer k.listener.Close()
1217 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1219 _, replicas, err := kc.PutB([]byte("foo"))
1223 c.Check(replicas, Equals, 2)
1226 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
1227 st := StubProxyHandler{make(chan string, 1)}
1229 arv, err := arvadosclient.MakeArvadosClient()
1231 kc, _ := MakeKeepClient(arv)
1233 kc.Want_replicas = 3
1234 arv.ApiToken = "abc123"
1235 localRoots := make(map[string]string)
1236 writableLocalRoots := make(map[string]string)
1238 ks1 := RunSomeFakeKeepServers(st, 1)
1240 for i, k := range ks1 {
1241 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1242 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1243 defer k.listener.Close()
1245 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1247 _, replicas, err := kc.PutB([]byte("foo"))
1250 c.Check(err, FitsTypeOf, InsufficientReplicasError{})
1251 c.Check(replicas, Equals, 2)
1254 func (s *StandaloneSuite) TestMakeLocator(c *C) {
1255 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
1257 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1258 c.Check(l.Size, Equals, 3)
1259 c.Check(l.Hints, DeepEquals, []string{"3", "Aabcde@12345678"})
1262 func (s *StandaloneSuite) TestMakeLocatorNoHints(c *C) {
1263 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce")
1265 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1266 c.Check(l.Size, Equals, -1)
1267 c.Check(l.Hints, DeepEquals, []string{})
1270 func (s *StandaloneSuite) TestMakeLocatorNoSizeHint(c *C) {
1271 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+Aabcde@12345678")
1273 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1274 c.Check(l.Size, Equals, -1)
1275 c.Check(l.Hints, DeepEquals, []string{"Aabcde@12345678"})
1278 func (s *StandaloneSuite) TestMakeLocatorPreservesUnrecognizedHints(c *C) {
1279 str := "91f372a266fe2bf2823cb8ec7fda31ce+3+Unknown+Kzzzzz+Afoobar"
1280 l, err := MakeLocator(str)
1282 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1283 c.Check(l.Size, Equals, 3)
1284 c.Check(l.Hints, DeepEquals, []string{"3", "Unknown", "Kzzzzz", "Afoobar"})
1285 c.Check(l.String(), Equals, str)
1288 func (s *StandaloneSuite) TestMakeLocatorInvalidInput(c *C) {
1289 _, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31c")
1290 c.Check(err, Equals, InvalidLocatorError)
1293 func (s *StandaloneSuite) TestPutBWant2ReplicasWithOnlyOneWritableLocalRoot(c *C) {
1294 hash := Md5String("foo")
1296 st := &StubPutHandler{
1299 expectAPIToken: "abc123",
1301 expectStorageClass: "default",
1302 returnStorageClasses: "",
1303 handled: make(chan string, 5),
1306 arv, _ := arvadosclient.MakeArvadosClient()
1307 kc, _ := MakeKeepClient(arv)
1309 kc.Want_replicas = 2
1310 arv.ApiToken = "abc123"
1311 localRoots := make(map[string]string)
1312 writableLocalRoots := make(map[string]string)
1314 ks := RunSomeFakeKeepServers(st, 5)
1316 for i, k := range ks {
1317 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1319 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1321 defer k.listener.Close()
1324 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1326 _, replicas, err := kc.PutB([]byte("foo"))
1328 c.Check(err, FitsTypeOf, InsufficientReplicasError{})
1329 c.Check(replicas, Equals, 1)
1331 c.Check(<-st.handled, Equals, localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", 0)])
1334 func (s *StandaloneSuite) TestPutBWithNoWritableLocalRoots(c *C) {
1335 hash := Md5String("foo")
1337 st := &StubPutHandler{
1340 expectAPIToken: "abc123",
1342 expectStorageClass: "",
1343 returnStorageClasses: "",
1344 handled: make(chan string, 5),
1347 arv, _ := arvadosclient.MakeArvadosClient()
1348 kc, _ := MakeKeepClient(arv)
1350 kc.Want_replicas = 2
1351 arv.ApiToken = "abc123"
1352 localRoots := make(map[string]string)
1353 writableLocalRoots := make(map[string]string)
1355 ks := RunSomeFakeKeepServers(st, 5)
1357 for i, k := range ks {
1358 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1359 defer k.listener.Close()
1362 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1364 _, replicas, err := kc.PutB([]byte("foo"))
1366 c.Check(err, FitsTypeOf, InsufficientReplicasError{})
1367 c.Check(replicas, Equals, 0)
1370 type StubGetIndexHandler struct {
1373 expectAPIToken string
1378 func (h StubGetIndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1379 h.c.Check(req.URL.Path, Equals, h.expectPath)
1380 h.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("Bearer %s", h.expectAPIToken))
1381 resp.WriteHeader(h.httpStatus)
1382 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(h.body)))
1386 func (s *StandaloneSuite) TestGetIndexWithNoPrefix(c *C) {
1387 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
1389 st := StubGetIndexHandler{
1394 []byte(hash + " 1443559274\n\n")}
1396 ks := RunFakeKeepServer(st)
1397 defer ks.listener.Close()
1399 arv, err := arvadosclient.MakeArvadosClient()
1400 c.Assert(err, IsNil)
1401 kc, err := MakeKeepClient(arv)
1402 c.Assert(err, IsNil)
1403 arv.ApiToken = "abc123"
1404 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1406 r, err := kc.GetIndex("x", "")
1409 content, err2 := ioutil.ReadAll(r)
1410 c.Check(err2, IsNil)
1411 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1414 func (s *StandaloneSuite) TestGetIndexWithPrefix(c *C) {
1415 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
1417 st := StubGetIndexHandler{
1419 "/index/" + hash[0:3],
1422 []byte(hash + " 1443559274\n\n")}
1424 ks := RunFakeKeepServer(st)
1425 defer ks.listener.Close()
1427 arv, err := arvadosclient.MakeArvadosClient()
1429 kc, _ := MakeKeepClient(arv)
1430 arv.ApiToken = "abc123"
1431 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1433 r, err := kc.GetIndex("x", hash[0:3])
1434 c.Assert(err, IsNil)
1436 content, err2 := ioutil.ReadAll(r)
1437 c.Check(err2, IsNil)
1438 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1441 func (s *StandaloneSuite) TestGetIndexIncomplete(c *C) {
1442 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
1444 st := StubGetIndexHandler{
1446 "/index/" + hash[0:3],
1451 ks := RunFakeKeepServer(st)
1452 defer ks.listener.Close()
1454 arv, err := arvadosclient.MakeArvadosClient()
1456 kc, _ := MakeKeepClient(arv)
1457 arv.ApiToken = "abc123"
1458 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1460 _, err = kc.GetIndex("x", hash[0:3])
1461 c.Check(err, Equals, ErrIncompleteIndex)
1464 func (s *StandaloneSuite) TestGetIndexWithNoSuchServer(c *C) {
1465 hash := fmt.Sprintf("%x+3", md5.Sum([]byte("foo")))
1467 st := StubGetIndexHandler{
1469 "/index/" + hash[0:3],
1474 ks := RunFakeKeepServer(st)
1475 defer ks.listener.Close()
1477 arv, err := arvadosclient.MakeArvadosClient()
1479 kc, _ := MakeKeepClient(arv)
1480 arv.ApiToken = "abc123"
1481 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1483 _, err = kc.GetIndex("y", hash[0:3])
1484 c.Check(err, Equals, ErrNoSuchKeepServer)
1487 func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
1488 st := StubGetIndexHandler{
1495 ks := RunFakeKeepServer(st)
1496 defer ks.listener.Close()
1498 arv, err := arvadosclient.MakeArvadosClient()
1500 kc, _ := MakeKeepClient(arv)
1501 arv.ApiToken = "abc123"
1502 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1504 r, err := kc.GetIndex("x", "abcd")
1507 content, err2 := ioutil.ReadAll(r)
1508 c.Check(err2, IsNil)
1509 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1512 func (s *StandaloneSuite) TestPutBRetry(c *C) {
1513 DefaultRetryDelay = time.Second / 8
1514 MinimumRetryDelay = time.Millisecond
1516 for _, delay := range []time.Duration{0, time.Nanosecond, time.Second / 8, time.Second / 16} {
1517 c.Logf("=== initial delay %v", delay)
1519 st := &FailThenSucceedHandler{
1520 morefails: 5, // handler will fail 6x in total, 3 for each server
1521 handled: make(chan string, 10),
1522 successhandler: &StubPutHandler{
1524 expectPath: Md5String("foo"),
1525 expectAPIToken: "abc123",
1527 expectStorageClass: "default",
1528 returnStorageClasses: "",
1529 handled: make(chan string, 5),
1533 arv, _ := arvadosclient.MakeArvadosClient()
1534 kc, _ := MakeKeepClient(arv)
1536 kc.RetryDelay = delay
1537 kc.DiskCacheSize = DiskCacheDisabled
1538 kc.Want_replicas = 2
1540 arv.ApiToken = "abc123"
1541 localRoots := make(map[string]string)
1542 writableLocalRoots := make(map[string]string)
1544 ks := RunSomeFakeKeepServers(st, 2)
1546 for i, k := range ks {
1547 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1548 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1549 defer k.listener.Close()
1552 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1555 hash, replicas, err := kc.PutB([]byte("foo"))
1558 c.Check(hash, Equals, "")
1559 c.Check(replicas, Equals, 2)
1560 elapsed := time.Since(t0)
1562 nonsleeptime := time.Second / 10
1563 expect := kc.RetryDelay
1565 expect = DefaultRetryDelay
1567 min := MinimumRetryDelay * 3
1568 max := expect + expect*2 + expect*2*2
1570 checkInterval(c, elapsed, min, max)
1574 func (s *ServerRequiredSuite) TestMakeKeepClientWithNonDiskTypeService(c *C) {
1575 arv, err := arvadosclient.MakeArvadosClient()
1576 c.Assert(err, IsNil)
1578 // Add an additional "testblobstore" keepservice
1579 blobKeepService := make(arvadosclient.Dict)
1580 err = arv.Create("keep_services",
1581 arvadosclient.Dict{"keep_service": arvadosclient.Dict{
1582 "service_host": "localhost",
1583 "service_port": "21321",
1584 "service_type": "testblobstore"}},
1586 c.Assert(err, IsNil)
1587 defer func() { arv.Delete("keep_services", blobKeepService["uuid"].(string), nil, nil) }()
1588 RefreshServiceDiscovery()
1590 // Make a keepclient and ensure that the testblobstore is included
1591 kc, err := MakeKeepClient(arv)
1592 c.Assert(err, IsNil)
1594 // verify kc.LocalRoots
1595 c.Check(len(kc.LocalRoots()), Equals, 3)
1596 for _, root := range kc.LocalRoots() {
1597 c.Check(root, Matches, "http://localhost:\\d+")
1599 c.Assert(kc.LocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1601 // verify kc.GatewayRoots
1602 c.Check(len(kc.GatewayRoots()), Equals, 3)
1603 for _, root := range kc.GatewayRoots() {
1604 c.Check(root, Matches, "http://localhost:\\d+")
1606 c.Assert(kc.GatewayRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1608 // verify kc.WritableLocalRoots
1609 c.Check(len(kc.WritableLocalRoots()), Equals, 3)
1610 for _, root := range kc.WritableLocalRoots() {
1611 c.Check(root, Matches, "http://localhost:\\d+")
1613 c.Assert(kc.WritableLocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1615 c.Assert(kc.replicasPerService, Equals, 0)
1616 c.Assert(kc.foundNonDiskSvc, Equals, true)
1617 c.Assert(kc.httpClient().(*http.Client).Timeout, Equals, 300*time.Second)
1620 func (s *StandaloneSuite) TestDelayCalculator_Default(c *C) {
1621 MinimumRetryDelay = time.Second / 2
1622 DefaultRetryDelay = time.Second
1624 dc := delayCalculator{InitialMaxDelay: 0}
1625 checkInterval(c, dc.Next(), time.Second/2, time.Second)
1626 checkInterval(c, dc.Next(), time.Second/2, time.Second*2)
1627 checkInterval(c, dc.Next(), time.Second/2, time.Second*4)
1628 checkInterval(c, dc.Next(), time.Second/2, time.Second*8)
1629 checkInterval(c, dc.Next(), time.Second/2, time.Second*10)
1630 checkInterval(c, dc.Next(), time.Second/2, time.Second*10)
1633 func (s *StandaloneSuite) TestDelayCalculator_SetInitial(c *C) {
1634 MinimumRetryDelay = time.Second / 2
1635 DefaultRetryDelay = time.Second
1637 dc := delayCalculator{InitialMaxDelay: time.Second * 2}
1638 checkInterval(c, dc.Next(), time.Second/2, time.Second*2)
1639 checkInterval(c, dc.Next(), time.Second/2, time.Second*4)
1640 checkInterval(c, dc.Next(), time.Second/2, time.Second*8)
1641 checkInterval(c, dc.Next(), time.Second/2, time.Second*16)
1642 checkInterval(c, dc.Next(), time.Second/2, time.Second*20)
1643 checkInterval(c, dc.Next(), time.Second/2, time.Second*20)
1644 checkInterval(c, dc.Next(), time.Second/2, time.Second*20)
1647 func (s *StandaloneSuite) TestDelayCalculator_EnsureSomeLongDelays(c *C) {
1648 dc := delayCalculator{InitialMaxDelay: time.Second * 5}
1651 for i := 0; i < n; i++ {
1652 if i < 20 || i%10 == 0 {
1653 c.Logf("i=%d, delay=%v", i, d)
1655 if d = dc.Next(); d > dc.InitialMaxDelay*9 {
1659 c.Errorf("after %d trials, never got a delay more than 90%% of expected max %d; last was %v", n, dc.InitialMaxDelay*10, d)
1662 // If InitialMaxDelay is less than MinimumRetryDelay/10, then delay is
1663 // always MinimumRetryDelay.
1664 func (s *StandaloneSuite) TestDelayCalculator_InitialLessThanMinimum(c *C) {
1665 MinimumRetryDelay = time.Second / 2
1666 dc := delayCalculator{InitialMaxDelay: time.Millisecond}
1667 for i := 0; i < 20; i++ {
1668 c.Check(dc.Next(), Equals, time.Second/2)
1672 func checkInterval(c *C, t, min, max time.Duration) {
1673 c.Check(t >= min, Equals, true, Commentf("got %v which is below expected min %v", t, min))
1674 c.Check(t <= max, Equals, true, Commentf("got %v which is above expected max %v", t, max))