1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
22 "git.curoverse.com/arvados.git/sdk/go/arvadosclient"
23 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
27 // Gocheck boilerplate
28 func Test(t *testing.T) {
32 // Gocheck boilerplate
33 var _ = Suite(&ServerRequiredSuite{})
34 var _ = Suite(&StandaloneSuite{})
36 // Tests that require the Keep server running
37 type ServerRequiredSuite struct{}
40 type StandaloneSuite struct{}
42 func (s *StandaloneSuite) SetUpTest(c *C) {
43 RefreshServiceDiscovery()
46 func pythonDir() string {
48 return fmt.Sprintf("%s/../../python/tests", cwd)
51 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
52 arvadostest.StartAPI()
53 arvadostest.StartKeep(2, false)
56 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
57 arvadostest.StopKeep(2)
61 func (s *ServerRequiredSuite) SetUpTest(c *C) {
62 RefreshServiceDiscovery()
65 func (s *ServerRequiredSuite) TestMakeKeepClient(c *C) {
66 arv, err := arvadosclient.MakeArvadosClient()
67 c.Assert(err, Equals, nil)
69 kc, err := MakeKeepClient(arv)
71 c.Assert(err, Equals, nil)
72 c.Check(len(kc.LocalRoots()), Equals, 2)
73 for _, root := range kc.LocalRoots() {
74 c.Check(root, Matches, "http://localhost:\\d+")
78 func (s *ServerRequiredSuite) TestDefaultReplications(c *C) {
79 arv, err := arvadosclient.MakeArvadosClient()
80 c.Assert(err, Equals, nil)
82 kc, err := MakeKeepClient(arv)
83 c.Assert(kc.Want_replicas, Equals, 2)
85 arv.DiscoveryDoc["defaultCollectionReplication"] = 3.0
86 kc, err = MakeKeepClient(arv)
87 c.Assert(kc.Want_replicas, Equals, 3)
89 arv.DiscoveryDoc["defaultCollectionReplication"] = 1.0
90 kc, err = MakeKeepClient(arv)
92 c.Assert(kc.Want_replicas, Equals, 1)
95 type StubPutHandler struct {
103 func (sph StubPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
104 sph.c.Check(req.URL.Path, Equals, "/"+sph.expectPath)
105 sph.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sph.expectApiToken))
106 body, err := ioutil.ReadAll(req.Body)
107 sph.c.Check(err, Equals, nil)
108 sph.c.Check(body, DeepEquals, []byte(sph.expectBody))
109 resp.WriteHeader(200)
110 sph.handled <- fmt.Sprintf("http://%s", req.Host)
113 func RunFakeKeepServer(st http.Handler) (ks KeepServer) {
115 // If we don't explicitly bind it to localhost, ks.listener.Addr() will
116 // bind to 0.0.0.0 or [::] which is not a valid address for Dial()
117 ks.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0})
119 panic(fmt.Sprintf("Could not listen on any port"))
121 ks.url = fmt.Sprintf("http://%s", ks.listener.Addr().String())
122 go http.Serve(ks.listener, st)
126 func UploadToStubHelper(c *C, st http.Handler, f func(*KeepClient, string,
127 io.ReadCloser, io.WriteCloser, chan uploadStatus)) {
129 ks := RunFakeKeepServer(st)
130 defer ks.listener.Close()
132 arv, _ := arvadosclient.MakeArvadosClient()
133 arv.ApiToken = "abc123"
135 kc, _ := MakeKeepClient(arv)
137 reader, writer := io.Pipe()
138 upload_status := make(chan uploadStatus)
140 f(kc, ks.url, reader, writer, upload_status)
143 func (s *StandaloneSuite) TestUploadToStubKeepServer(c *C) {
144 log.Printf("TestUploadToStubKeepServer")
146 st := StubPutHandler{
148 "acbd18db4cc2f85cedef654fccc4a4d8",
153 UploadToStubHelper(c, st,
154 func(kc *KeepClient, url string, reader io.ReadCloser, writer io.WriteCloser, upload_status chan uploadStatus) {
156 go kc.uploadToKeepServer(url, st.expectPath, reader, upload_status, int64(len("foo")), kc.getRequestID())
158 writer.Write([]byte("foo"))
162 status := <-upload_status
163 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
167 func (s *StandaloneSuite) TestUploadToStubKeepServerBufferReader(c *C) {
168 st := StubPutHandler{
170 "acbd18db4cc2f85cedef654fccc4a4d8",
175 UploadToStubHelper(c, st,
176 func(kc *KeepClient, url string, _ io.ReadCloser, _ io.WriteCloser, upload_status chan uploadStatus) {
177 go kc.uploadToKeepServer(url, st.expectPath, bytes.NewBuffer([]byte("foo")), upload_status, 3, kc.getRequestID())
181 status := <-upload_status
182 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
186 type FailHandler struct {
190 func (fh FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
191 resp.WriteHeader(500)
192 fh.handled <- fmt.Sprintf("http://%s", req.Host)
195 type FailThenSucceedHandler struct {
198 successhandler StubGetHandler
201 func (fh *FailThenSucceedHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
203 resp.WriteHeader(500)
205 fh.handled <- fmt.Sprintf("http://%s", req.Host)
207 fh.successhandler.ServeHTTP(resp, req)
211 type Error404Handler struct {
215 func (fh Error404Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
216 resp.WriteHeader(404)
217 fh.handled <- fmt.Sprintf("http://%s", req.Host)
220 func (s *StandaloneSuite) TestFailedUploadToStubKeepServer(c *C) {
224 hash := "acbd18db4cc2f85cedef654fccc4a4d8"
226 UploadToStubHelper(c, st,
227 func(kc *KeepClient, url string, reader io.ReadCloser,
228 writer io.WriteCloser, upload_status chan uploadStatus) {
230 go kc.uploadToKeepServer(url, hash, reader, upload_status, 3, kc.getRequestID())
232 writer.Write([]byte("foo"))
237 status := <-upload_status
238 c.Check(status.url, Equals, fmt.Sprintf("%s/%s", url, hash))
239 c.Check(status.statusCode, Equals, 500)
243 type KeepServer struct {
244 listener net.Listener
248 func RunSomeFakeKeepServers(st http.Handler, n int) (ks []KeepServer) {
249 ks = make([]KeepServer, n)
251 for i := 0; i < n; i += 1 {
252 ks[i] = RunFakeKeepServer(st)
258 func (s *StandaloneSuite) TestPutB(c *C) {
259 hash := Md5String("foo")
261 st := StubPutHandler{
266 make(chan string, 5)}
268 arv, _ := arvadosclient.MakeArvadosClient()
269 kc, _ := MakeKeepClient(arv)
272 arv.ApiToken = "abc123"
273 localRoots := make(map[string]string)
274 writableLocalRoots := make(map[string]string)
276 ks := RunSomeFakeKeepServers(st, 5)
278 for i, k := range ks {
279 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
280 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
281 defer k.listener.Close()
284 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
286 kc.PutB([]byte("foo"))
288 shuff := NewRootSorter(
289 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
293 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
294 (s1 == shuff[1] && s2 == shuff[0]),
299 func (s *StandaloneSuite) TestPutHR(c *C) {
300 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
302 st := StubPutHandler{
307 make(chan string, 5)}
309 arv, _ := arvadosclient.MakeArvadosClient()
310 kc, _ := MakeKeepClient(arv)
313 arv.ApiToken = "abc123"
314 localRoots := make(map[string]string)
315 writableLocalRoots := make(map[string]string)
317 ks := RunSomeFakeKeepServers(st, 5)
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()
325 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
327 reader, writer := io.Pipe()
330 writer.Write([]byte("foo"))
334 kc.PutHR(hash, reader, 3)
336 shuff := NewRootSorter(kc.LocalRoots(), hash).GetSortedRoots()
341 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
342 (s1 == shuff[1] && s2 == shuff[0]),
347 func (s *StandaloneSuite) TestPutWithFail(c *C) {
348 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
350 st := StubPutHandler{
355 make(chan string, 4)}
358 make(chan string, 1)}
360 arv, err := arvadosclient.MakeArvadosClient()
361 kc, _ := MakeKeepClient(arv)
364 arv.ApiToken = "abc123"
365 localRoots := make(map[string]string)
366 writableLocalRoots := make(map[string]string)
368 ks1 := RunSomeFakeKeepServers(st, 4)
369 ks2 := RunSomeFakeKeepServers(fh, 1)
371 for i, k := range ks1 {
372 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
373 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
374 defer k.listener.Close()
376 for i, k := range ks2 {
377 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
378 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
379 defer k.listener.Close()
382 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
384 shuff := NewRootSorter(
385 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
388 phash, replicas, err := kc.PutB([]byte("foo"))
392 c.Check(err, Equals, nil)
393 c.Check(phash, Equals, "")
394 c.Check(replicas, Equals, 2)
399 c.Check((s1 == shuff[1] && s2 == shuff[2]) ||
400 (s1 == shuff[2] && s2 == shuff[1]),
405 func (s *StandaloneSuite) TestPutWithTooManyFail(c *C) {
406 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
408 st := StubPutHandler{
413 make(chan string, 1)}
416 make(chan string, 4)}
418 arv, err := arvadosclient.MakeArvadosClient()
419 kc, _ := MakeKeepClient(arv)
423 arv.ApiToken = "abc123"
424 localRoots := make(map[string]string)
425 writableLocalRoots := make(map[string]string)
427 ks1 := RunSomeFakeKeepServers(st, 1)
428 ks2 := RunSomeFakeKeepServers(fh, 4)
430 for i, k := range ks1 {
431 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
432 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
433 defer k.listener.Close()
435 for i, k := range ks2 {
436 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
437 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
438 defer k.listener.Close()
441 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
443 _, replicas, err := kc.PutB([]byte("foo"))
445 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
446 c.Check(replicas, Equals, 1)
447 c.Check(<-st.handled, Equals, ks1[0].url)
450 type StubGetHandler struct {
453 expectApiToken string
458 func (sgh StubGetHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
459 sgh.c.Check(req.URL.Path, Equals, "/"+sgh.expectPath)
460 sgh.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sgh.expectApiToken))
461 resp.WriteHeader(sgh.httpStatus)
462 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(sgh.body)))
466 func (s *StandaloneSuite) TestGet(c *C) {
467 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
469 st := StubGetHandler{
476 ks := RunFakeKeepServer(st)
477 defer ks.listener.Close()
479 arv, err := arvadosclient.MakeArvadosClient()
480 kc, _ := MakeKeepClient(arv)
481 arv.ApiToken = "abc123"
482 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
484 r, n, url2, err := kc.Get(hash)
486 c.Check(err, Equals, nil)
487 c.Check(n, Equals, int64(3))
488 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
490 content, err2 := ioutil.ReadAll(r)
491 c.Check(err2, Equals, nil)
492 c.Check(content, DeepEquals, []byte("foo"))
495 func (s *StandaloneSuite) TestGet404(c *C) {
496 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
498 st := Error404Handler{make(chan string, 1)}
500 ks := RunFakeKeepServer(st)
501 defer ks.listener.Close()
503 arv, err := arvadosclient.MakeArvadosClient()
504 kc, _ := MakeKeepClient(arv)
505 arv.ApiToken = "abc123"
506 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
508 r, n, url2, err := kc.Get(hash)
509 c.Check(err, Equals, BlockNotFound)
510 c.Check(n, Equals, int64(0))
511 c.Check(url2, Equals, "")
512 c.Check(r, Equals, nil)
515 func (s *StandaloneSuite) TestGetEmptyBlock(c *C) {
516 st := Error404Handler{make(chan string, 1)}
518 ks := RunFakeKeepServer(st)
519 defer ks.listener.Close()
521 arv, err := arvadosclient.MakeArvadosClient()
522 kc, _ := MakeKeepClient(arv)
523 arv.ApiToken = "abc123"
524 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
526 r, n, url2, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e+0")
528 c.Check(n, Equals, int64(0))
529 c.Check(url2, Equals, "")
531 buf, err := ioutil.ReadAll(r)
533 c.Check(buf, DeepEquals, []byte{})
536 func (s *StandaloneSuite) TestGetFail(c *C) {
537 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
539 st := FailHandler{make(chan string, 1)}
541 ks := RunFakeKeepServer(st)
542 defer ks.listener.Close()
544 arv, err := arvadosclient.MakeArvadosClient()
545 kc, _ := MakeKeepClient(arv)
546 arv.ApiToken = "abc123"
547 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
550 r, n, url2, err := kc.Get(hash)
551 errNotFound, _ := err.(*ErrNotFound)
552 c.Check(errNotFound, NotNil)
553 c.Check(strings.Contains(errNotFound.Error(), "HTTP 500"), Equals, true)
554 c.Check(errNotFound.Temporary(), Equals, true)
555 c.Check(n, Equals, int64(0))
556 c.Check(url2, Equals, "")
557 c.Check(r, Equals, nil)
560 func (s *StandaloneSuite) TestGetFailRetry(c *C) {
561 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
563 st := &FailThenSucceedHandler{make(chan string, 1), 0,
571 ks := RunFakeKeepServer(st)
572 defer ks.listener.Close()
574 arv, err := arvadosclient.MakeArvadosClient()
575 kc, _ := MakeKeepClient(arv)
576 arv.ApiToken = "abc123"
577 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
579 r, n, url2, err := kc.Get(hash)
581 c.Check(err, Equals, nil)
582 c.Check(n, Equals, int64(3))
583 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
585 content, err2 := ioutil.ReadAll(r)
586 c.Check(err2, Equals, nil)
587 c.Check(content, DeepEquals, []byte("foo"))
590 func (s *StandaloneSuite) TestGetNetError(c *C) {
591 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
593 arv, err := arvadosclient.MakeArvadosClient()
594 kc, _ := MakeKeepClient(arv)
595 arv.ApiToken = "abc123"
596 kc.SetServiceRoots(map[string]string{"x": "http://localhost:62222"}, nil, nil)
598 r, n, url2, err := kc.Get(hash)
599 errNotFound, _ := err.(*ErrNotFound)
600 c.Check(errNotFound, NotNil)
601 c.Check(strings.Contains(errNotFound.Error(), "connection refused"), Equals, true)
602 c.Check(errNotFound.Temporary(), Equals, true)
603 c.Check(n, Equals, int64(0))
604 c.Check(url2, Equals, "")
605 c.Check(r, Equals, nil)
608 func (s *StandaloneSuite) TestGetWithServiceHint(c *C) {
609 uuid := "zzzzz-bi6l4-123451234512345"
610 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
612 // This one shouldn't be used:
613 ks0 := RunFakeKeepServer(StubGetHandler{
619 defer ks0.listener.Close()
620 // This one should be used:
621 ks := RunFakeKeepServer(StubGetHandler{
627 defer ks.listener.Close()
629 arv, err := arvadosclient.MakeArvadosClient()
630 kc, _ := MakeKeepClient(arv)
631 arv.ApiToken = "abc123"
633 map[string]string{"x": ks0.url},
635 map[string]string{uuid: ks.url})
637 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
639 c.Check(err, Equals, nil)
640 c.Check(n, Equals, int64(3))
641 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
643 content, err := ioutil.ReadAll(r)
644 c.Check(err, Equals, nil)
645 c.Check(content, DeepEquals, []byte("foo"))
648 // Use a service hint to fetch from a local disk service, overriding
649 // rendezvous probe order.
650 func (s *StandaloneSuite) TestGetWithLocalServiceHint(c *C) {
651 uuid := "zzzzz-bi6l4-zzzzzzzzzzzzzzz"
652 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
654 // This one shouldn't be used, although it appears first in
655 // rendezvous probe order:
656 ks0 := RunFakeKeepServer(StubGetHandler{
662 defer ks0.listener.Close()
663 // This one should be used:
664 ks := RunFakeKeepServer(StubGetHandler{
670 defer ks.listener.Close()
672 arv, err := arvadosclient.MakeArvadosClient()
673 kc, _ := MakeKeepClient(arv)
674 arv.ApiToken = "abc123"
677 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
678 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
679 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
683 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
684 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
685 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
689 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
691 c.Check(err, Equals, nil)
692 c.Check(n, Equals, int64(3))
693 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
695 content, err := ioutil.ReadAll(r)
696 c.Check(err, Equals, nil)
697 c.Check(content, DeepEquals, []byte("foo"))
700 func (s *StandaloneSuite) TestGetWithServiceHintFailoverToLocals(c *C) {
701 uuid := "zzzzz-bi6l4-123451234512345"
702 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
704 ksLocal := RunFakeKeepServer(StubGetHandler{
710 defer ksLocal.listener.Close()
711 ksGateway := RunFakeKeepServer(StubGetHandler{
715 http.StatusInternalServerError,
717 defer ksGateway.listener.Close()
719 arv, err := arvadosclient.MakeArvadosClient()
720 kc, _ := MakeKeepClient(arv)
721 arv.ApiToken = "abc123"
723 map[string]string{"zzzzz-bi6l4-keepdisk0000000": ksLocal.url},
725 map[string]string{uuid: ksGateway.url})
727 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
728 c.Assert(err, Equals, nil)
730 c.Check(n, Equals, int64(3))
731 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ksLocal.url, hash+"+K@"+uuid))
733 content, err := ioutil.ReadAll(r)
734 c.Check(err, Equals, nil)
735 c.Check(content, DeepEquals, []byte("foo"))
738 type BarHandler struct {
742 func (this BarHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
743 resp.Write([]byte("bar"))
744 this.handled <- fmt.Sprintf("http://%s", req.Host)
747 func (s *StandaloneSuite) TestChecksum(c *C) {
748 foohash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
749 barhash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
751 st := BarHandler{make(chan string, 1)}
753 ks := RunFakeKeepServer(st)
754 defer ks.listener.Close()
756 arv, err := arvadosclient.MakeArvadosClient()
757 kc, _ := MakeKeepClient(arv)
758 arv.ApiToken = "abc123"
759 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
761 r, n, _, err := kc.Get(barhash)
762 _, err = ioutil.ReadAll(r)
763 c.Check(n, Equals, int64(3))
764 c.Check(err, Equals, nil)
768 r, n, _, err = kc.Get(foohash)
769 _, err = ioutil.ReadAll(r)
770 c.Check(n, Equals, int64(3))
771 c.Check(err, Equals, BadChecksum)
776 func (s *StandaloneSuite) TestGetWithFailures(c *C) {
777 content := []byte("waz")
778 hash := fmt.Sprintf("%x", md5.Sum(content))
780 fh := Error404Handler{
781 make(chan string, 4)}
783 st := StubGetHandler{
790 arv, err := arvadosclient.MakeArvadosClient()
791 kc, _ := MakeKeepClient(arv)
792 arv.ApiToken = "abc123"
793 localRoots := make(map[string]string)
794 writableLocalRoots := make(map[string]string)
796 ks1 := RunSomeFakeKeepServers(st, 1)
797 ks2 := RunSomeFakeKeepServers(fh, 4)
799 for i, k := range ks1 {
800 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
801 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
802 defer k.listener.Close()
804 for i, k := range ks2 {
805 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
806 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
807 defer k.listener.Close()
810 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
813 // This test works only if one of the failing services is
814 // attempted before the succeeding service. Otherwise,
815 // <-fh.handled below will just hang! (Probe order depends on
816 // the choice of block content "waz" and the UUIDs of the fake
817 // servers, so we just tried different strings until we found
818 // an example that passes this Assert.)
819 c.Assert(NewRootSorter(localRoots, hash).GetSortedRoots()[0], Not(Equals), ks1[0].url)
821 r, n, url2, err := kc.Get(hash)
824 c.Check(err, Equals, nil)
825 c.Check(n, Equals, int64(3))
826 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks1[0].url, hash))
828 read_content, err2 := ioutil.ReadAll(r)
829 c.Check(err2, Equals, nil)
830 c.Check(read_content, DeepEquals, content)
833 func (s *ServerRequiredSuite) TestPutGetHead(c *C) {
834 content := []byte("TestPutGetHead")
836 arv, err := arvadosclient.MakeArvadosClient()
837 kc, err := MakeKeepClient(arv)
838 c.Assert(err, Equals, nil)
840 hash := fmt.Sprintf("%x", md5.Sum(content))
843 n, _, err := kc.Ask(hash)
844 c.Check(err, Equals, BlockNotFound)
845 c.Check(n, Equals, int64(0))
848 hash2, replicas, err := kc.PutB(content)
849 c.Check(hash2, Matches, fmt.Sprintf(`%s\+%d\b.*`, hash, len(content)))
850 c.Check(replicas, Equals, 2)
851 c.Check(err, Equals, nil)
854 r, n, url2, err := kc.Get(hash)
855 c.Check(err, Equals, nil)
856 c.Check(n, Equals, int64(len(content)))
857 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
859 read_content, err2 := ioutil.ReadAll(r)
860 c.Check(err2, Equals, nil)
861 c.Check(read_content, DeepEquals, content)
864 n, url2, err := kc.Ask(hash)
865 c.Check(err, Equals, nil)
866 c.Check(n, Equals, int64(len(content)))
867 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
871 type StubProxyHandler struct {
875 func (this StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
876 resp.Header().Set("X-Keep-Replicas-Stored", "2")
877 this.handled <- fmt.Sprintf("http://%s", req.Host)
880 func (s *StandaloneSuite) TestPutProxy(c *C) {
881 st := StubProxyHandler{make(chan string, 1)}
883 arv, err := arvadosclient.MakeArvadosClient()
884 kc, _ := MakeKeepClient(arv)
887 arv.ApiToken = "abc123"
888 localRoots := make(map[string]string)
889 writableLocalRoots := make(map[string]string)
891 ks1 := RunSomeFakeKeepServers(st, 1)
893 for i, k := range ks1 {
894 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
895 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
896 defer k.listener.Close()
899 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
901 _, replicas, err := kc.PutB([]byte("foo"))
904 c.Check(err, Equals, nil)
905 c.Check(replicas, Equals, 2)
908 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
909 st := StubProxyHandler{make(chan string, 1)}
911 arv, err := arvadosclient.MakeArvadosClient()
912 kc, _ := MakeKeepClient(arv)
915 arv.ApiToken = "abc123"
916 localRoots := make(map[string]string)
917 writableLocalRoots := make(map[string]string)
919 ks1 := RunSomeFakeKeepServers(st, 1)
921 for i, k := range ks1 {
922 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
923 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
924 defer k.listener.Close()
926 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
928 _, replicas, err := kc.PutB([]byte("foo"))
931 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
932 c.Check(replicas, Equals, 2)
935 func (s *StandaloneSuite) TestMakeLocator(c *C) {
936 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
937 c.Check(err, Equals, nil)
938 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
939 c.Check(l.Size, Equals, 3)
940 c.Check(l.Hints, DeepEquals, []string{"3", "Aabcde@12345678"})
943 func (s *StandaloneSuite) TestMakeLocatorNoHints(c *C) {
944 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce")
945 c.Check(err, Equals, nil)
946 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
947 c.Check(l.Size, Equals, -1)
948 c.Check(l.Hints, DeepEquals, []string{})
951 func (s *StandaloneSuite) TestMakeLocatorNoSizeHint(c *C) {
952 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+Aabcde@12345678")
953 c.Check(err, Equals, nil)
954 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
955 c.Check(l.Size, Equals, -1)
956 c.Check(l.Hints, DeepEquals, []string{"Aabcde@12345678"})
959 func (s *StandaloneSuite) TestMakeLocatorPreservesUnrecognizedHints(c *C) {
960 str := "91f372a266fe2bf2823cb8ec7fda31ce+3+Unknown+Kzzzzz+Afoobar"
961 l, err := MakeLocator(str)
962 c.Check(err, Equals, nil)
963 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
964 c.Check(l.Size, Equals, 3)
965 c.Check(l.Hints, DeepEquals, []string{"3", "Unknown", "Kzzzzz", "Afoobar"})
966 c.Check(l.String(), Equals, str)
969 func (s *StandaloneSuite) TestMakeLocatorInvalidInput(c *C) {
970 _, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31c")
971 c.Check(err, Equals, InvalidLocatorError)
974 func (s *StandaloneSuite) TestPutBWant2ReplicasWithOnlyOneWritableLocalRoot(c *C) {
975 hash := Md5String("foo")
977 st := StubPutHandler{
982 make(chan string, 5)}
984 arv, _ := arvadosclient.MakeArvadosClient()
985 kc, _ := MakeKeepClient(arv)
988 arv.ApiToken = "abc123"
989 localRoots := make(map[string]string)
990 writableLocalRoots := make(map[string]string)
992 ks := RunSomeFakeKeepServers(st, 5)
994 for i, k := range ks {
995 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
997 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
999 defer k.listener.Close()
1002 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1004 _, replicas, err := kc.PutB([]byte("foo"))
1006 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1007 c.Check(replicas, Equals, 1)
1009 c.Check(<-st.handled, Equals, localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", 0)])
1012 func (s *StandaloneSuite) TestPutBWithNoWritableLocalRoots(c *C) {
1013 hash := Md5String("foo")
1015 st := StubPutHandler{
1020 make(chan string, 5)}
1022 arv, _ := arvadosclient.MakeArvadosClient()
1023 kc, _ := MakeKeepClient(arv)
1025 kc.Want_replicas = 2
1026 arv.ApiToken = "abc123"
1027 localRoots := make(map[string]string)
1028 writableLocalRoots := make(map[string]string)
1030 ks := RunSomeFakeKeepServers(st, 5)
1032 for i, k := range ks {
1033 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1034 defer k.listener.Close()
1037 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1039 _, replicas, err := kc.PutB([]byte("foo"))
1041 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1042 c.Check(replicas, Equals, 0)
1045 type StubGetIndexHandler struct {
1048 expectAPIToken string
1053 func (h StubGetIndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1054 h.c.Check(req.URL.Path, Equals, h.expectPath)
1055 h.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", h.expectAPIToken))
1056 resp.WriteHeader(h.httpStatus)
1057 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(h.body)))
1061 func (s *StandaloneSuite) TestGetIndexWithNoPrefix(c *C) {
1062 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1064 st := StubGetIndexHandler{
1069 []byte(hash + "+3 1443559274\n\n")}
1071 ks := RunFakeKeepServer(st)
1072 defer ks.listener.Close()
1074 arv, err := arvadosclient.MakeArvadosClient()
1075 c.Assert(err, IsNil)
1076 kc, err := MakeKeepClient(arv)
1077 c.Assert(err, IsNil)
1078 arv.ApiToken = "abc123"
1079 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1081 r, err := kc.GetIndex("x", "")
1084 content, err2 := ioutil.ReadAll(r)
1085 c.Check(err2, Equals, nil)
1086 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1089 func (s *StandaloneSuite) TestGetIndexWithPrefix(c *C) {
1090 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1092 st := StubGetIndexHandler{
1094 "/index/" + hash[0:3],
1097 []byte(hash + "+3 1443559274\n\n")}
1099 ks := RunFakeKeepServer(st)
1100 defer ks.listener.Close()
1102 arv, err := arvadosclient.MakeArvadosClient()
1103 kc, _ := MakeKeepClient(arv)
1104 arv.ApiToken = "abc123"
1105 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1107 r, err := kc.GetIndex("x", hash[0:3])
1108 c.Assert(err, Equals, nil)
1110 content, err2 := ioutil.ReadAll(r)
1111 c.Check(err2, Equals, nil)
1112 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1115 func (s *StandaloneSuite) TestGetIndexIncomplete(c *C) {
1116 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1118 st := StubGetIndexHandler{
1120 "/index/" + hash[0:3],
1125 ks := RunFakeKeepServer(st)
1126 defer ks.listener.Close()
1128 arv, err := arvadosclient.MakeArvadosClient()
1129 kc, _ := MakeKeepClient(arv)
1130 arv.ApiToken = "abc123"
1131 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1133 _, err = kc.GetIndex("x", hash[0:3])
1134 c.Check(err, Equals, ErrIncompleteIndex)
1137 func (s *StandaloneSuite) TestGetIndexWithNoSuchServer(c *C) {
1138 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1140 st := StubGetIndexHandler{
1142 "/index/" + hash[0:3],
1147 ks := RunFakeKeepServer(st)
1148 defer ks.listener.Close()
1150 arv, err := arvadosclient.MakeArvadosClient()
1151 kc, _ := MakeKeepClient(arv)
1152 arv.ApiToken = "abc123"
1153 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1155 _, err = kc.GetIndex("y", hash[0:3])
1156 c.Check(err, Equals, ErrNoSuchKeepServer)
1159 func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
1160 st := StubGetIndexHandler{
1167 ks := RunFakeKeepServer(st)
1168 defer ks.listener.Close()
1170 arv, err := arvadosclient.MakeArvadosClient()
1171 kc, _ := MakeKeepClient(arv)
1172 arv.ApiToken = "abc123"
1173 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1175 r, err := kc.GetIndex("x", "abcd")
1176 c.Check(err, Equals, nil)
1178 content, err2 := ioutil.ReadAll(r)
1179 c.Check(err2, Equals, nil)
1180 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1183 type FailThenSucceedPutHandler struct {
1186 successhandler StubPutHandler
1189 func (h *FailThenSucceedPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1191 resp.WriteHeader(500)
1193 h.handled <- fmt.Sprintf("http://%s", req.Host)
1195 h.successhandler.ServeHTTP(resp, req)
1199 func (s *StandaloneSuite) TestPutBRetry(c *C) {
1200 st := &FailThenSucceedPutHandler{make(chan string, 1), 0,
1206 make(chan string, 5)}}
1208 arv, _ := arvadosclient.MakeArvadosClient()
1209 kc, _ := MakeKeepClient(arv)
1211 kc.Want_replicas = 2
1212 arv.ApiToken = "abc123"
1213 localRoots := make(map[string]string)
1214 writableLocalRoots := make(map[string]string)
1216 ks := RunSomeFakeKeepServers(st, 2)
1218 for i, k := range ks {
1219 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1220 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1221 defer k.listener.Close()
1224 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1226 hash, replicas, err := kc.PutB([]byte("foo"))
1228 c.Check(err, Equals, nil)
1229 c.Check(hash, Equals, "")
1230 c.Check(replicas, Equals, 2)
1233 func (s *ServerRequiredSuite) TestMakeKeepClientWithNonDiskTypeService(c *C) {
1234 arv, err := arvadosclient.MakeArvadosClient()
1235 c.Assert(err, Equals, nil)
1237 // Add an additional "testblobstore" keepservice
1238 blobKeepService := make(arvadosclient.Dict)
1239 err = arv.Create("keep_services",
1240 arvadosclient.Dict{"keep_service": arvadosclient.Dict{
1241 "service_host": "localhost",
1242 "service_port": "21321",
1243 "service_type": "testblobstore"}},
1245 c.Assert(err, Equals, nil)
1246 defer func() { arv.Delete("keep_services", blobKeepService["uuid"].(string), nil, nil) }()
1247 RefreshServiceDiscovery()
1249 // Make a keepclient and ensure that the testblobstore is included
1250 kc, err := MakeKeepClient(arv)
1251 c.Assert(err, Equals, nil)
1253 // verify kc.LocalRoots
1254 c.Check(len(kc.LocalRoots()), Equals, 3)
1255 for _, root := range kc.LocalRoots() {
1256 c.Check(root, Matches, "http://localhost:\\d+")
1258 c.Assert(kc.LocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1260 // verify kc.GatewayRoots
1261 c.Check(len(kc.GatewayRoots()), Equals, 3)
1262 for _, root := range kc.GatewayRoots() {
1263 c.Check(root, Matches, "http://localhost:\\d+")
1265 c.Assert(kc.GatewayRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1267 // verify kc.WritableLocalRoots
1268 c.Check(len(kc.WritableLocalRoots()), Equals, 3)
1269 for _, root := range kc.WritableLocalRoots() {
1270 c.Check(root, Matches, "http://localhost:\\d+")
1272 c.Assert(kc.WritableLocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1274 c.Assert(kc.replicasPerService, Equals, 0)
1275 c.Assert(kc.foundNonDiskSvc, Equals, true)
1276 c.Assert(kc.httpClient().(*http.Client).Timeout, Equals, 300*time.Second)