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 {
100 expectStorageClass string
104 func (sph StubPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
105 sph.c.Check(req.URL.Path, Equals, "/"+sph.expectPath)
106 sph.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sph.expectApiToken))
107 sph.c.Check(req.Header.Get("X-Keep-Storage-Classes"), Equals, sph.expectStorageClass)
108 body, err := ioutil.ReadAll(req.Body)
109 sph.c.Check(err, Equals, nil)
110 sph.c.Check(body, DeepEquals, []byte(sph.expectBody))
111 resp.WriteHeader(200)
112 sph.handled <- fmt.Sprintf("http://%s", req.Host)
115 func RunFakeKeepServer(st http.Handler) (ks KeepServer) {
117 // If we don't explicitly bind it to localhost, ks.listener.Addr() will
118 // bind to 0.0.0.0 or [::] which is not a valid address for Dial()
119 ks.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0})
121 panic(fmt.Sprintf("Could not listen on any port"))
123 ks.url = fmt.Sprintf("http://%s", ks.listener.Addr().String())
124 go http.Serve(ks.listener, st)
128 func UploadToStubHelper(c *C, st http.Handler, f func(*KeepClient, string,
129 io.ReadCloser, io.WriteCloser, chan uploadStatus)) {
131 ks := RunFakeKeepServer(st)
132 defer ks.listener.Close()
134 arv, _ := arvadosclient.MakeArvadosClient()
135 arv.ApiToken = "abc123"
137 kc, _ := MakeKeepClient(arv)
139 reader, writer := io.Pipe()
140 upload_status := make(chan uploadStatus)
142 f(kc, ks.url, reader, writer, upload_status)
145 func (s *StandaloneSuite) TestUploadToStubKeepServer(c *C) {
146 log.Printf("TestUploadToStubKeepServer")
148 st := StubPutHandler{
150 "acbd18db4cc2f85cedef654fccc4a4d8",
156 UploadToStubHelper(c, st,
157 func(kc *KeepClient, url string, reader io.ReadCloser, writer io.WriteCloser, upload_status chan uploadStatus) {
158 kc.StorageClasses = []string{"hot"}
159 go kc.uploadToKeepServer(url, st.expectPath, reader, upload_status, int64(len("foo")), kc.getRequestID())
161 writer.Write([]byte("foo"))
165 status := <-upload_status
166 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
170 func (s *StandaloneSuite) TestUploadToStubKeepServerBufferReader(c *C) {
171 st := StubPutHandler{
173 "acbd18db4cc2f85cedef654fccc4a4d8",
179 UploadToStubHelper(c, st,
180 func(kc *KeepClient, url string, _ io.ReadCloser, _ io.WriteCloser, upload_status chan uploadStatus) {
181 go kc.uploadToKeepServer(url, st.expectPath, bytes.NewBuffer([]byte("foo")), upload_status, 3, kc.getRequestID())
185 status := <-upload_status
186 c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
190 type FailHandler struct {
194 func (fh FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
195 resp.WriteHeader(500)
196 fh.handled <- fmt.Sprintf("http://%s", req.Host)
199 type FailThenSucceedHandler struct {
202 successhandler http.Handler
206 func (fh *FailThenSucceedHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
207 fh.reqIDs = append(fh.reqIDs, req.Header.Get("X-Request-Id"))
209 resp.WriteHeader(500)
211 fh.handled <- fmt.Sprintf("http://%s", req.Host)
213 fh.successhandler.ServeHTTP(resp, req)
217 type Error404Handler struct {
221 func (fh Error404Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
222 resp.WriteHeader(404)
223 fh.handled <- fmt.Sprintf("http://%s", req.Host)
226 func (s *StandaloneSuite) TestFailedUploadToStubKeepServer(c *C) {
230 hash := "acbd18db4cc2f85cedef654fccc4a4d8"
232 UploadToStubHelper(c, st,
233 func(kc *KeepClient, url string, reader io.ReadCloser,
234 writer io.WriteCloser, upload_status chan uploadStatus) {
236 go kc.uploadToKeepServer(url, hash, reader, upload_status, 3, kc.getRequestID())
238 writer.Write([]byte("foo"))
243 status := <-upload_status
244 c.Check(status.url, Equals, fmt.Sprintf("%s/%s", url, hash))
245 c.Check(status.statusCode, Equals, 500)
249 type KeepServer struct {
250 listener net.Listener
254 func RunSomeFakeKeepServers(st http.Handler, n int) (ks []KeepServer) {
255 ks = make([]KeepServer, n)
257 for i := 0; i < n; i += 1 {
258 ks[i] = RunFakeKeepServer(st)
264 func (s *StandaloneSuite) TestPutB(c *C) {
265 hash := Md5String("foo")
267 st := StubPutHandler{
273 make(chan string, 5)}
275 arv, _ := arvadosclient.MakeArvadosClient()
276 kc, _ := MakeKeepClient(arv)
279 arv.ApiToken = "abc123"
280 localRoots := make(map[string]string)
281 writableLocalRoots := make(map[string]string)
283 ks := RunSomeFakeKeepServers(st, 5)
285 for i, k := range ks {
286 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
287 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
288 defer k.listener.Close()
291 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
293 kc.PutB([]byte("foo"))
295 shuff := NewRootSorter(
296 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
300 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
301 (s1 == shuff[1] && s2 == shuff[0]),
306 func (s *StandaloneSuite) TestPutHR(c *C) {
307 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
309 st := StubPutHandler{
315 make(chan string, 5)}
317 arv, _ := arvadosclient.MakeArvadosClient()
318 kc, _ := MakeKeepClient(arv)
321 arv.ApiToken = "abc123"
322 localRoots := make(map[string]string)
323 writableLocalRoots := make(map[string]string)
325 ks := RunSomeFakeKeepServers(st, 5)
327 for i, k := range ks {
328 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
329 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
330 defer k.listener.Close()
333 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
335 reader, writer := io.Pipe()
338 writer.Write([]byte("foo"))
342 kc.PutHR(hash, reader, 3)
344 shuff := NewRootSorter(kc.LocalRoots(), hash).GetSortedRoots()
349 c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
350 (s1 == shuff[1] && s2 == shuff[0]),
355 func (s *StandaloneSuite) TestPutWithFail(c *C) {
356 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
358 st := StubPutHandler{
364 make(chan string, 4)}
367 make(chan string, 1)}
369 arv, err := arvadosclient.MakeArvadosClient()
370 kc, _ := MakeKeepClient(arv)
373 arv.ApiToken = "abc123"
374 localRoots := make(map[string]string)
375 writableLocalRoots := make(map[string]string)
377 ks1 := RunSomeFakeKeepServers(st, 4)
378 ks2 := RunSomeFakeKeepServers(fh, 1)
380 for i, k := range ks1 {
381 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
382 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
383 defer k.listener.Close()
385 for i, k := range ks2 {
386 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
387 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
388 defer k.listener.Close()
391 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
393 shuff := NewRootSorter(
394 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
397 phash, replicas, err := kc.PutB([]byte("foo"))
401 c.Check(err, Equals, nil)
402 c.Check(phash, Equals, "")
403 c.Check(replicas, Equals, 2)
408 c.Check((s1 == shuff[1] && s2 == shuff[2]) ||
409 (s1 == shuff[2] && s2 == shuff[1]),
414 func (s *StandaloneSuite) TestPutWithTooManyFail(c *C) {
415 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
417 st := StubPutHandler{
423 make(chan string, 1)}
426 make(chan string, 4)}
428 arv, err := arvadosclient.MakeArvadosClient()
429 kc, _ := MakeKeepClient(arv)
433 arv.ApiToken = "abc123"
434 localRoots := make(map[string]string)
435 writableLocalRoots := make(map[string]string)
437 ks1 := RunSomeFakeKeepServers(st, 1)
438 ks2 := RunSomeFakeKeepServers(fh, 4)
440 for i, k := range ks1 {
441 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
442 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
443 defer k.listener.Close()
445 for i, k := range ks2 {
446 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
447 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
448 defer k.listener.Close()
451 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
453 _, replicas, err := kc.PutB([]byte("foo"))
455 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
456 c.Check(replicas, Equals, 1)
457 c.Check(<-st.handled, Equals, ks1[0].url)
460 type StubGetHandler struct {
463 expectApiToken string
468 func (sgh StubGetHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
469 sgh.c.Check(req.URL.Path, Equals, "/"+sgh.expectPath)
470 sgh.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sgh.expectApiToken))
471 resp.WriteHeader(sgh.httpStatus)
472 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(sgh.body)))
476 func (s *StandaloneSuite) TestGet(c *C) {
477 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
479 st := StubGetHandler{
486 ks := RunFakeKeepServer(st)
487 defer ks.listener.Close()
489 arv, err := arvadosclient.MakeArvadosClient()
490 kc, _ := MakeKeepClient(arv)
491 arv.ApiToken = "abc123"
492 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
494 r, n, url2, err := kc.Get(hash)
496 c.Check(err, Equals, nil)
497 c.Check(n, Equals, int64(3))
498 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
500 content, err2 := ioutil.ReadAll(r)
501 c.Check(err2, Equals, nil)
502 c.Check(content, DeepEquals, []byte("foo"))
505 func (s *StandaloneSuite) TestGet404(c *C) {
506 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
508 st := Error404Handler{make(chan string, 1)}
510 ks := RunFakeKeepServer(st)
511 defer ks.listener.Close()
513 arv, err := arvadosclient.MakeArvadosClient()
514 kc, _ := MakeKeepClient(arv)
515 arv.ApiToken = "abc123"
516 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
518 r, n, url2, err := kc.Get(hash)
519 c.Check(err, Equals, BlockNotFound)
520 c.Check(n, Equals, int64(0))
521 c.Check(url2, Equals, "")
522 c.Check(r, Equals, nil)
525 func (s *StandaloneSuite) TestGetEmptyBlock(c *C) {
526 st := Error404Handler{make(chan string, 1)}
528 ks := RunFakeKeepServer(st)
529 defer ks.listener.Close()
531 arv, err := arvadosclient.MakeArvadosClient()
532 kc, _ := MakeKeepClient(arv)
533 arv.ApiToken = "abc123"
534 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
536 r, n, url2, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e+0")
538 c.Check(n, Equals, int64(0))
539 c.Check(url2, Equals, "")
541 buf, err := ioutil.ReadAll(r)
543 c.Check(buf, DeepEquals, []byte{})
546 func (s *StandaloneSuite) TestGetFail(c *C) {
547 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
549 st := FailHandler{make(chan string, 1)}
551 ks := RunFakeKeepServer(st)
552 defer ks.listener.Close()
554 arv, err := arvadosclient.MakeArvadosClient()
555 kc, _ := MakeKeepClient(arv)
556 arv.ApiToken = "abc123"
557 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
560 r, n, url2, err := kc.Get(hash)
561 errNotFound, _ := err.(*ErrNotFound)
562 c.Check(errNotFound, NotNil)
563 c.Check(strings.Contains(errNotFound.Error(), "HTTP 500"), Equals, true)
564 c.Check(errNotFound.Temporary(), Equals, true)
565 c.Check(n, Equals, int64(0))
566 c.Check(url2, Equals, "")
567 c.Check(r, Equals, nil)
570 func (s *StandaloneSuite) TestGetFailRetry(c *C) {
571 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
573 st := &FailThenSucceedHandler{
574 handled: make(chan string, 1),
575 successhandler: StubGetHandler{
582 ks := RunFakeKeepServer(st)
583 defer ks.listener.Close()
585 arv, err := arvadosclient.MakeArvadosClient()
586 kc, _ := MakeKeepClient(arv)
587 arv.ApiToken = "abc123"
588 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
590 r, n, url2, err := kc.Get(hash)
592 c.Check(err, Equals, nil)
593 c.Check(n, Equals, int64(3))
594 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
596 content, err2 := ioutil.ReadAll(r)
597 c.Check(err2, Equals, nil)
598 c.Check(content, DeepEquals, []byte("foo"))
600 c.Logf("%q", st.reqIDs)
601 c.Assert(len(st.reqIDs) > 1, Equals, true)
602 for _, reqid := range st.reqIDs {
603 c.Check(reqid, Not(Equals), "")
604 c.Check(reqid, Equals, st.reqIDs[0])
608 func (s *StandaloneSuite) TestGetNetError(c *C) {
609 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
611 arv, err := arvadosclient.MakeArvadosClient()
612 kc, _ := MakeKeepClient(arv)
613 arv.ApiToken = "abc123"
614 kc.SetServiceRoots(map[string]string{"x": "http://localhost:62222"}, nil, nil)
616 r, n, url2, err := kc.Get(hash)
617 errNotFound, _ := err.(*ErrNotFound)
618 c.Check(errNotFound, NotNil)
619 c.Check(strings.Contains(errNotFound.Error(), "connection refused"), Equals, true)
620 c.Check(errNotFound.Temporary(), Equals, true)
621 c.Check(n, Equals, int64(0))
622 c.Check(url2, Equals, "")
623 c.Check(r, Equals, nil)
626 func (s *StandaloneSuite) TestGetWithServiceHint(c *C) {
627 uuid := "zzzzz-bi6l4-123451234512345"
628 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
630 // This one shouldn't be used:
631 ks0 := RunFakeKeepServer(StubGetHandler{
637 defer ks0.listener.Close()
638 // This one should be used:
639 ks := RunFakeKeepServer(StubGetHandler{
645 defer ks.listener.Close()
647 arv, err := arvadosclient.MakeArvadosClient()
648 kc, _ := MakeKeepClient(arv)
649 arv.ApiToken = "abc123"
651 map[string]string{"x": ks0.url},
653 map[string]string{uuid: ks.url})
655 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
657 c.Check(err, Equals, nil)
658 c.Check(n, Equals, int64(3))
659 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
661 content, err := ioutil.ReadAll(r)
662 c.Check(err, Equals, nil)
663 c.Check(content, DeepEquals, []byte("foo"))
666 // Use a service hint to fetch from a local disk service, overriding
667 // rendezvous probe order.
668 func (s *StandaloneSuite) TestGetWithLocalServiceHint(c *C) {
669 uuid := "zzzzz-bi6l4-zzzzzzzzzzzzzzz"
670 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
672 // This one shouldn't be used, although it appears first in
673 // rendezvous probe order:
674 ks0 := RunFakeKeepServer(StubGetHandler{
680 defer ks0.listener.Close()
681 // This one should be used:
682 ks := RunFakeKeepServer(StubGetHandler{
688 defer ks.listener.Close()
690 arv, err := arvadosclient.MakeArvadosClient()
691 kc, _ := MakeKeepClient(arv)
692 arv.ApiToken = "abc123"
695 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
696 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
697 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
701 "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
702 "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
703 "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
707 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
709 c.Check(err, Equals, nil)
710 c.Check(n, Equals, int64(3))
711 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
713 content, err := ioutil.ReadAll(r)
714 c.Check(err, Equals, nil)
715 c.Check(content, DeepEquals, []byte("foo"))
718 func (s *StandaloneSuite) TestGetWithServiceHintFailoverToLocals(c *C) {
719 uuid := "zzzzz-bi6l4-123451234512345"
720 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
722 ksLocal := RunFakeKeepServer(StubGetHandler{
728 defer ksLocal.listener.Close()
729 ksGateway := RunFakeKeepServer(StubGetHandler{
733 http.StatusInternalServerError,
735 defer ksGateway.listener.Close()
737 arv, err := arvadosclient.MakeArvadosClient()
738 kc, _ := MakeKeepClient(arv)
739 arv.ApiToken = "abc123"
741 map[string]string{"zzzzz-bi6l4-keepdisk0000000": ksLocal.url},
743 map[string]string{uuid: ksGateway.url})
745 r, n, uri, err := kc.Get(hash + "+K@" + uuid)
746 c.Assert(err, Equals, nil)
748 c.Check(n, Equals, int64(3))
749 c.Check(uri, Equals, fmt.Sprintf("%s/%s", ksLocal.url, hash+"+K@"+uuid))
751 content, err := ioutil.ReadAll(r)
752 c.Check(err, Equals, nil)
753 c.Check(content, DeepEquals, []byte("foo"))
756 type BarHandler struct {
760 func (this BarHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
761 resp.Write([]byte("bar"))
762 this.handled <- fmt.Sprintf("http://%s", req.Host)
765 func (s *StandaloneSuite) TestChecksum(c *C) {
766 foohash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
767 barhash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
769 st := BarHandler{make(chan string, 1)}
771 ks := RunFakeKeepServer(st)
772 defer ks.listener.Close()
774 arv, err := arvadosclient.MakeArvadosClient()
775 kc, _ := MakeKeepClient(arv)
776 arv.ApiToken = "abc123"
777 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
779 r, n, _, err := kc.Get(barhash)
780 _, err = ioutil.ReadAll(r)
781 c.Check(n, Equals, int64(3))
782 c.Check(err, Equals, nil)
786 r, n, _, err = kc.Get(foohash)
787 _, err = ioutil.ReadAll(r)
788 c.Check(n, Equals, int64(3))
789 c.Check(err, Equals, BadChecksum)
794 func (s *StandaloneSuite) TestGetWithFailures(c *C) {
795 content := []byte("waz")
796 hash := fmt.Sprintf("%x", md5.Sum(content))
798 fh := Error404Handler{
799 make(chan string, 4)}
801 st := StubGetHandler{
808 arv, err := arvadosclient.MakeArvadosClient()
809 kc, _ := MakeKeepClient(arv)
810 arv.ApiToken = "abc123"
811 localRoots := make(map[string]string)
812 writableLocalRoots := make(map[string]string)
814 ks1 := RunSomeFakeKeepServers(st, 1)
815 ks2 := RunSomeFakeKeepServers(fh, 4)
817 for i, k := range ks1 {
818 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
819 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
820 defer k.listener.Close()
822 for i, k := range ks2 {
823 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
824 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
825 defer k.listener.Close()
828 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
831 // This test works only if one of the failing services is
832 // attempted before the succeeding service. Otherwise,
833 // <-fh.handled below will just hang! (Probe order depends on
834 // the choice of block content "waz" and the UUIDs of the fake
835 // servers, so we just tried different strings until we found
836 // an example that passes this Assert.)
837 c.Assert(NewRootSorter(localRoots, hash).GetSortedRoots()[0], Not(Equals), ks1[0].url)
839 r, n, url2, err := kc.Get(hash)
842 c.Check(err, Equals, nil)
843 c.Check(n, Equals, int64(3))
844 c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks1[0].url, hash))
846 read_content, err2 := ioutil.ReadAll(r)
847 c.Check(err2, Equals, nil)
848 c.Check(read_content, DeepEquals, content)
851 func (s *ServerRequiredSuite) TestPutGetHead(c *C) {
852 content := []byte("TestPutGetHead")
854 arv, err := arvadosclient.MakeArvadosClient()
855 kc, err := MakeKeepClient(arv)
856 c.Assert(err, Equals, nil)
858 hash := fmt.Sprintf("%x", md5.Sum(content))
861 n, _, err := kc.Ask(hash)
862 c.Check(err, Equals, BlockNotFound)
863 c.Check(n, Equals, int64(0))
866 hash2, replicas, err := kc.PutB(content)
867 c.Check(hash2, Matches, fmt.Sprintf(`%s\+%d\b.*`, hash, len(content)))
868 c.Check(replicas, Equals, 2)
869 c.Check(err, Equals, nil)
872 r, n, url2, err := kc.Get(hash)
873 c.Check(err, Equals, nil)
874 c.Check(n, Equals, int64(len(content)))
875 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
877 read_content, err2 := ioutil.ReadAll(r)
878 c.Check(err2, Equals, nil)
879 c.Check(read_content, DeepEquals, content)
882 n, url2, err := kc.Ask(hash)
883 c.Check(err, Equals, nil)
884 c.Check(n, Equals, int64(len(content)))
885 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
889 type StubProxyHandler struct {
893 func (this StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
894 resp.Header().Set("X-Keep-Replicas-Stored", "2")
895 this.handled <- fmt.Sprintf("http://%s", req.Host)
898 func (s *StandaloneSuite) TestPutProxy(c *C) {
899 st := StubProxyHandler{make(chan string, 1)}
901 arv, err := arvadosclient.MakeArvadosClient()
902 kc, _ := MakeKeepClient(arv)
905 arv.ApiToken = "abc123"
906 localRoots := make(map[string]string)
907 writableLocalRoots := make(map[string]string)
909 ks1 := RunSomeFakeKeepServers(st, 1)
911 for i, k := range ks1 {
912 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
913 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
914 defer k.listener.Close()
917 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
919 _, replicas, err := kc.PutB([]byte("foo"))
922 c.Check(err, Equals, nil)
923 c.Check(replicas, Equals, 2)
926 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
927 st := StubProxyHandler{make(chan string, 1)}
929 arv, err := arvadosclient.MakeArvadosClient()
930 kc, _ := MakeKeepClient(arv)
933 arv.ApiToken = "abc123"
934 localRoots := make(map[string]string)
935 writableLocalRoots := make(map[string]string)
937 ks1 := RunSomeFakeKeepServers(st, 1)
939 for i, k := range ks1 {
940 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
941 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
942 defer k.listener.Close()
944 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
946 _, replicas, err := kc.PutB([]byte("foo"))
949 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
950 c.Check(replicas, Equals, 2)
953 func (s *StandaloneSuite) TestMakeLocator(c *C) {
954 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
955 c.Check(err, Equals, nil)
956 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
957 c.Check(l.Size, Equals, 3)
958 c.Check(l.Hints, DeepEquals, []string{"3", "Aabcde@12345678"})
961 func (s *StandaloneSuite) TestMakeLocatorNoHints(c *C) {
962 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce")
963 c.Check(err, Equals, nil)
964 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
965 c.Check(l.Size, Equals, -1)
966 c.Check(l.Hints, DeepEquals, []string{})
969 func (s *StandaloneSuite) TestMakeLocatorNoSizeHint(c *C) {
970 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+Aabcde@12345678")
971 c.Check(err, Equals, nil)
972 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
973 c.Check(l.Size, Equals, -1)
974 c.Check(l.Hints, DeepEquals, []string{"Aabcde@12345678"})
977 func (s *StandaloneSuite) TestMakeLocatorPreservesUnrecognizedHints(c *C) {
978 str := "91f372a266fe2bf2823cb8ec7fda31ce+3+Unknown+Kzzzzz+Afoobar"
979 l, err := MakeLocator(str)
980 c.Check(err, Equals, nil)
981 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
982 c.Check(l.Size, Equals, 3)
983 c.Check(l.Hints, DeepEquals, []string{"3", "Unknown", "Kzzzzz", "Afoobar"})
984 c.Check(l.String(), Equals, str)
987 func (s *StandaloneSuite) TestMakeLocatorInvalidInput(c *C) {
988 _, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31c")
989 c.Check(err, Equals, InvalidLocatorError)
992 func (s *StandaloneSuite) TestPutBWant2ReplicasWithOnlyOneWritableLocalRoot(c *C) {
993 hash := Md5String("foo")
995 st := StubPutHandler{
1001 make(chan string, 5)}
1003 arv, _ := arvadosclient.MakeArvadosClient()
1004 kc, _ := MakeKeepClient(arv)
1006 kc.Want_replicas = 2
1007 arv.ApiToken = "abc123"
1008 localRoots := make(map[string]string)
1009 writableLocalRoots := make(map[string]string)
1011 ks := RunSomeFakeKeepServers(st, 5)
1013 for i, k := range ks {
1014 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1016 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1018 defer k.listener.Close()
1021 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1023 _, replicas, err := kc.PutB([]byte("foo"))
1025 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1026 c.Check(replicas, Equals, 1)
1028 c.Check(<-st.handled, Equals, localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", 0)])
1031 func (s *StandaloneSuite) TestPutBWithNoWritableLocalRoots(c *C) {
1032 hash := Md5String("foo")
1034 st := StubPutHandler{
1040 make(chan string, 5)}
1042 arv, _ := arvadosclient.MakeArvadosClient()
1043 kc, _ := MakeKeepClient(arv)
1045 kc.Want_replicas = 2
1046 arv.ApiToken = "abc123"
1047 localRoots := make(map[string]string)
1048 writableLocalRoots := make(map[string]string)
1050 ks := RunSomeFakeKeepServers(st, 5)
1052 for i, k := range ks {
1053 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1054 defer k.listener.Close()
1057 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1059 _, replicas, err := kc.PutB([]byte("foo"))
1061 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1062 c.Check(replicas, Equals, 0)
1065 type StubGetIndexHandler struct {
1068 expectAPIToken string
1073 func (h StubGetIndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1074 h.c.Check(req.URL.Path, Equals, h.expectPath)
1075 h.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", h.expectAPIToken))
1076 resp.WriteHeader(h.httpStatus)
1077 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(h.body)))
1081 func (s *StandaloneSuite) TestGetIndexWithNoPrefix(c *C) {
1082 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1084 st := StubGetIndexHandler{
1089 []byte(hash + "+3 1443559274\n\n")}
1091 ks := RunFakeKeepServer(st)
1092 defer ks.listener.Close()
1094 arv, err := arvadosclient.MakeArvadosClient()
1095 c.Assert(err, IsNil)
1096 kc, err := MakeKeepClient(arv)
1097 c.Assert(err, IsNil)
1098 arv.ApiToken = "abc123"
1099 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1101 r, err := kc.GetIndex("x", "")
1104 content, err2 := ioutil.ReadAll(r)
1105 c.Check(err2, Equals, nil)
1106 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1109 func (s *StandaloneSuite) TestGetIndexWithPrefix(c *C) {
1110 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1112 st := StubGetIndexHandler{
1114 "/index/" + hash[0:3],
1117 []byte(hash + "+3 1443559274\n\n")}
1119 ks := RunFakeKeepServer(st)
1120 defer ks.listener.Close()
1122 arv, err := arvadosclient.MakeArvadosClient()
1123 kc, _ := MakeKeepClient(arv)
1124 arv.ApiToken = "abc123"
1125 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1127 r, err := kc.GetIndex("x", hash[0:3])
1128 c.Assert(err, Equals, nil)
1130 content, err2 := ioutil.ReadAll(r)
1131 c.Check(err2, Equals, nil)
1132 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1135 func (s *StandaloneSuite) TestGetIndexIncomplete(c *C) {
1136 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1138 st := StubGetIndexHandler{
1140 "/index/" + hash[0:3],
1145 ks := RunFakeKeepServer(st)
1146 defer ks.listener.Close()
1148 arv, err := arvadosclient.MakeArvadosClient()
1149 kc, _ := MakeKeepClient(arv)
1150 arv.ApiToken = "abc123"
1151 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1153 _, err = kc.GetIndex("x", hash[0:3])
1154 c.Check(err, Equals, ErrIncompleteIndex)
1157 func (s *StandaloneSuite) TestGetIndexWithNoSuchServer(c *C) {
1158 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1160 st := StubGetIndexHandler{
1162 "/index/" + hash[0:3],
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 _, err = kc.GetIndex("y", hash[0:3])
1176 c.Check(err, Equals, ErrNoSuchKeepServer)
1179 func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
1180 st := StubGetIndexHandler{
1187 ks := RunFakeKeepServer(st)
1188 defer ks.listener.Close()
1190 arv, err := arvadosclient.MakeArvadosClient()
1191 kc, _ := MakeKeepClient(arv)
1192 arv.ApiToken = "abc123"
1193 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1195 r, err := kc.GetIndex("x", "abcd")
1196 c.Check(err, Equals, nil)
1198 content, err2 := ioutil.ReadAll(r)
1199 c.Check(err2, Equals, nil)
1200 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1203 func (s *StandaloneSuite) TestPutBRetry(c *C) {
1204 st := &FailThenSucceedHandler{
1205 handled: make(chan string, 1),
1206 successhandler: StubPutHandler{
1212 make(chan string, 5)}}
1214 arv, _ := arvadosclient.MakeArvadosClient()
1215 kc, _ := MakeKeepClient(arv)
1217 kc.Want_replicas = 2
1218 arv.ApiToken = "abc123"
1219 localRoots := make(map[string]string)
1220 writableLocalRoots := make(map[string]string)
1222 ks := RunSomeFakeKeepServers(st, 2)
1224 for i, k := range ks {
1225 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1226 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1227 defer k.listener.Close()
1230 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1232 hash, replicas, err := kc.PutB([]byte("foo"))
1234 c.Check(err, Equals, nil)
1235 c.Check(hash, Equals, "")
1236 c.Check(replicas, Equals, 2)
1239 func (s *ServerRequiredSuite) TestMakeKeepClientWithNonDiskTypeService(c *C) {
1240 arv, err := arvadosclient.MakeArvadosClient()
1241 c.Assert(err, Equals, nil)
1243 // Add an additional "testblobstore" keepservice
1244 blobKeepService := make(arvadosclient.Dict)
1245 err = arv.Create("keep_services",
1246 arvadosclient.Dict{"keep_service": arvadosclient.Dict{
1247 "service_host": "localhost",
1248 "service_port": "21321",
1249 "service_type": "testblobstore"}},
1251 c.Assert(err, Equals, nil)
1252 defer func() { arv.Delete("keep_services", blobKeepService["uuid"].(string), nil, nil) }()
1253 RefreshServiceDiscovery()
1255 // Make a keepclient and ensure that the testblobstore is included
1256 kc, err := MakeKeepClient(arv)
1257 c.Assert(err, Equals, nil)
1259 // verify kc.LocalRoots
1260 c.Check(len(kc.LocalRoots()), Equals, 3)
1261 for _, root := range kc.LocalRoots() {
1262 c.Check(root, Matches, "http://localhost:\\d+")
1264 c.Assert(kc.LocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1266 // verify kc.GatewayRoots
1267 c.Check(len(kc.GatewayRoots()), Equals, 3)
1268 for _, root := range kc.GatewayRoots() {
1269 c.Check(root, Matches, "http://localhost:\\d+")
1271 c.Assert(kc.GatewayRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1273 // verify kc.WritableLocalRoots
1274 c.Check(len(kc.WritableLocalRoots()), Equals, 3)
1275 for _, root := range kc.WritableLocalRoots() {
1276 c.Check(root, Matches, "http://localhost:\\d+")
1278 c.Assert(kc.WritableLocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1280 c.Assert(kc.replicasPerService, Equals, 0)
1281 c.Assert(kc.foundNonDiskSvc, Equals, true)
1282 c.Assert(kc.httpClient().(*http.Client).Timeout, Equals, 300*time.Second)