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))
888 loc, err := kc.LocalLocator(hash)
889 c.Check(err, Equals, nil)
890 c.Assert(len(loc) >= 32, Equals, true)
891 c.Check(loc[:32], Equals, hash[:32])
894 content := []byte("the perth county conspiracy")
895 loc, err := kc.LocalLocator(fmt.Sprintf("%x+%d+Rzaaaa-abcde@12345", md5.Sum(content), len(content)))
896 c.Check(loc, Equals, "")
897 c.Check(err, ErrorMatches, `.*HEAD .*\+R.*`)
898 c.Check(err, ErrorMatches, `.*HTTP 400.*`)
902 type StubProxyHandler struct {
906 func (this StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
907 resp.Header().Set("X-Keep-Replicas-Stored", "2")
908 this.handled <- fmt.Sprintf("http://%s", req.Host)
911 func (s *StandaloneSuite) TestPutProxy(c *C) {
912 st := StubProxyHandler{make(chan string, 1)}
914 arv, err := arvadosclient.MakeArvadosClient()
915 kc, _ := MakeKeepClient(arv)
918 arv.ApiToken = "abc123"
919 localRoots := make(map[string]string)
920 writableLocalRoots := make(map[string]string)
922 ks1 := RunSomeFakeKeepServers(st, 1)
924 for i, k := range ks1 {
925 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
926 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
927 defer k.listener.Close()
930 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
932 _, replicas, err := kc.PutB([]byte("foo"))
935 c.Check(err, Equals, nil)
936 c.Check(replicas, Equals, 2)
939 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
940 st := StubProxyHandler{make(chan string, 1)}
942 arv, err := arvadosclient.MakeArvadosClient()
943 kc, _ := MakeKeepClient(arv)
946 arv.ApiToken = "abc123"
947 localRoots := make(map[string]string)
948 writableLocalRoots := make(map[string]string)
950 ks1 := RunSomeFakeKeepServers(st, 1)
952 for i, k := range ks1 {
953 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
954 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
955 defer k.listener.Close()
957 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
959 _, replicas, err := kc.PutB([]byte("foo"))
962 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
963 c.Check(replicas, Equals, 2)
966 func (s *StandaloneSuite) TestMakeLocator(c *C) {
967 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
968 c.Check(err, Equals, nil)
969 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
970 c.Check(l.Size, Equals, 3)
971 c.Check(l.Hints, DeepEquals, []string{"3", "Aabcde@12345678"})
974 func (s *StandaloneSuite) TestMakeLocatorNoHints(c *C) {
975 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce")
976 c.Check(err, Equals, nil)
977 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
978 c.Check(l.Size, Equals, -1)
979 c.Check(l.Hints, DeepEquals, []string{})
982 func (s *StandaloneSuite) TestMakeLocatorNoSizeHint(c *C) {
983 l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+Aabcde@12345678")
984 c.Check(err, Equals, nil)
985 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
986 c.Check(l.Size, Equals, -1)
987 c.Check(l.Hints, DeepEquals, []string{"Aabcde@12345678"})
990 func (s *StandaloneSuite) TestMakeLocatorPreservesUnrecognizedHints(c *C) {
991 str := "91f372a266fe2bf2823cb8ec7fda31ce+3+Unknown+Kzzzzz+Afoobar"
992 l, err := MakeLocator(str)
993 c.Check(err, Equals, nil)
994 c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
995 c.Check(l.Size, Equals, 3)
996 c.Check(l.Hints, DeepEquals, []string{"3", "Unknown", "Kzzzzz", "Afoobar"})
997 c.Check(l.String(), Equals, str)
1000 func (s *StandaloneSuite) TestMakeLocatorInvalidInput(c *C) {
1001 _, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31c")
1002 c.Check(err, Equals, InvalidLocatorError)
1005 func (s *StandaloneSuite) TestPutBWant2ReplicasWithOnlyOneWritableLocalRoot(c *C) {
1006 hash := Md5String("foo")
1008 st := StubPutHandler{
1014 make(chan string, 5)}
1016 arv, _ := arvadosclient.MakeArvadosClient()
1017 kc, _ := MakeKeepClient(arv)
1019 kc.Want_replicas = 2
1020 arv.ApiToken = "abc123"
1021 localRoots := make(map[string]string)
1022 writableLocalRoots := make(map[string]string)
1024 ks := RunSomeFakeKeepServers(st, 5)
1026 for i, k := range ks {
1027 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1029 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1031 defer k.listener.Close()
1034 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1036 _, replicas, err := kc.PutB([]byte("foo"))
1038 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1039 c.Check(replicas, Equals, 1)
1041 c.Check(<-st.handled, Equals, localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", 0)])
1044 func (s *StandaloneSuite) TestPutBWithNoWritableLocalRoots(c *C) {
1045 hash := Md5String("foo")
1047 st := StubPutHandler{
1053 make(chan string, 5)}
1055 arv, _ := arvadosclient.MakeArvadosClient()
1056 kc, _ := MakeKeepClient(arv)
1058 kc.Want_replicas = 2
1059 arv.ApiToken = "abc123"
1060 localRoots := make(map[string]string)
1061 writableLocalRoots := make(map[string]string)
1063 ks := RunSomeFakeKeepServers(st, 5)
1065 for i, k := range ks {
1066 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1067 defer k.listener.Close()
1070 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1072 _, replicas, err := kc.PutB([]byte("foo"))
1074 c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1075 c.Check(replicas, Equals, 0)
1078 type StubGetIndexHandler struct {
1081 expectAPIToken string
1086 func (h StubGetIndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1087 h.c.Check(req.URL.Path, Equals, h.expectPath)
1088 h.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", h.expectAPIToken))
1089 resp.WriteHeader(h.httpStatus)
1090 resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(h.body)))
1094 func (s *StandaloneSuite) TestGetIndexWithNoPrefix(c *C) {
1095 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1097 st := StubGetIndexHandler{
1102 []byte(hash + "+3 1443559274\n\n")}
1104 ks := RunFakeKeepServer(st)
1105 defer ks.listener.Close()
1107 arv, err := arvadosclient.MakeArvadosClient()
1108 c.Assert(err, IsNil)
1109 kc, err := MakeKeepClient(arv)
1110 c.Assert(err, IsNil)
1111 arv.ApiToken = "abc123"
1112 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1114 r, err := kc.GetIndex("x", "")
1117 content, err2 := ioutil.ReadAll(r)
1118 c.Check(err2, Equals, nil)
1119 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1122 func (s *StandaloneSuite) TestGetIndexWithPrefix(c *C) {
1123 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1125 st := StubGetIndexHandler{
1127 "/index/" + hash[0:3],
1130 []byte(hash + "+3 1443559274\n\n")}
1132 ks := RunFakeKeepServer(st)
1133 defer ks.listener.Close()
1135 arv, err := arvadosclient.MakeArvadosClient()
1136 kc, _ := MakeKeepClient(arv)
1137 arv.ApiToken = "abc123"
1138 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1140 r, err := kc.GetIndex("x", hash[0:3])
1141 c.Assert(err, Equals, nil)
1143 content, err2 := ioutil.ReadAll(r)
1144 c.Check(err2, Equals, nil)
1145 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1148 func (s *StandaloneSuite) TestGetIndexIncomplete(c *C) {
1149 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1151 st := StubGetIndexHandler{
1153 "/index/" + hash[0:3],
1158 ks := RunFakeKeepServer(st)
1159 defer ks.listener.Close()
1161 arv, err := arvadosclient.MakeArvadosClient()
1162 kc, _ := MakeKeepClient(arv)
1163 arv.ApiToken = "abc123"
1164 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1166 _, err = kc.GetIndex("x", hash[0:3])
1167 c.Check(err, Equals, ErrIncompleteIndex)
1170 func (s *StandaloneSuite) TestGetIndexWithNoSuchServer(c *C) {
1171 hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1173 st := StubGetIndexHandler{
1175 "/index/" + hash[0:3],
1180 ks := RunFakeKeepServer(st)
1181 defer ks.listener.Close()
1183 arv, err := arvadosclient.MakeArvadosClient()
1184 kc, _ := MakeKeepClient(arv)
1185 arv.ApiToken = "abc123"
1186 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1188 _, err = kc.GetIndex("y", hash[0:3])
1189 c.Check(err, Equals, ErrNoSuchKeepServer)
1192 func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
1193 st := StubGetIndexHandler{
1200 ks := RunFakeKeepServer(st)
1201 defer ks.listener.Close()
1203 arv, err := arvadosclient.MakeArvadosClient()
1204 kc, _ := MakeKeepClient(arv)
1205 arv.ApiToken = "abc123"
1206 kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1208 r, err := kc.GetIndex("x", "abcd")
1209 c.Check(err, Equals, nil)
1211 content, err2 := ioutil.ReadAll(r)
1212 c.Check(err2, Equals, nil)
1213 c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1216 func (s *StandaloneSuite) TestPutBRetry(c *C) {
1217 st := &FailThenSucceedHandler{
1218 handled: make(chan string, 1),
1219 successhandler: StubPutHandler{
1225 make(chan string, 5)}}
1227 arv, _ := arvadosclient.MakeArvadosClient()
1228 kc, _ := MakeKeepClient(arv)
1230 kc.Want_replicas = 2
1231 arv.ApiToken = "abc123"
1232 localRoots := make(map[string]string)
1233 writableLocalRoots := make(map[string]string)
1235 ks := RunSomeFakeKeepServers(st, 2)
1237 for i, k := range ks {
1238 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1239 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1240 defer k.listener.Close()
1243 kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1245 hash, replicas, err := kc.PutB([]byte("foo"))
1247 c.Check(err, Equals, nil)
1248 c.Check(hash, Equals, "")
1249 c.Check(replicas, Equals, 2)
1252 func (s *ServerRequiredSuite) TestMakeKeepClientWithNonDiskTypeService(c *C) {
1253 arv, err := arvadosclient.MakeArvadosClient()
1254 c.Assert(err, Equals, nil)
1256 // Add an additional "testblobstore" keepservice
1257 blobKeepService := make(arvadosclient.Dict)
1258 err = arv.Create("keep_services",
1259 arvadosclient.Dict{"keep_service": arvadosclient.Dict{
1260 "service_host": "localhost",
1261 "service_port": "21321",
1262 "service_type": "testblobstore"}},
1264 c.Assert(err, Equals, nil)
1265 defer func() { arv.Delete("keep_services", blobKeepService["uuid"].(string), nil, nil) }()
1266 RefreshServiceDiscovery()
1268 // Make a keepclient and ensure that the testblobstore is included
1269 kc, err := MakeKeepClient(arv)
1270 c.Assert(err, Equals, nil)
1272 // verify kc.LocalRoots
1273 c.Check(len(kc.LocalRoots()), Equals, 3)
1274 for _, root := range kc.LocalRoots() {
1275 c.Check(root, Matches, "http://localhost:\\d+")
1277 c.Assert(kc.LocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1279 // verify kc.GatewayRoots
1280 c.Check(len(kc.GatewayRoots()), Equals, 3)
1281 for _, root := range kc.GatewayRoots() {
1282 c.Check(root, Matches, "http://localhost:\\d+")
1284 c.Assert(kc.GatewayRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1286 // verify kc.WritableLocalRoots
1287 c.Check(len(kc.WritableLocalRoots()), Equals, 3)
1288 for _, root := range kc.WritableLocalRoots() {
1289 c.Check(root, Matches, "http://localhost:\\d+")
1291 c.Assert(kc.WritableLocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1293 c.Assert(kc.replicasPerService, Equals, 0)
1294 c.Assert(kc.foundNonDiskSvc, Equals, true)
1295 c.Assert(kc.httpClient().(*http.Client).Timeout, Equals, 300*time.Second)