Fix more ineffassign warnings.
[arvados.git] / sdk / go / keepclient / keepclient_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package keepclient
6
7 import (
8         "bytes"
9         "crypto/md5"
10         "errors"
11         "fmt"
12         "io"
13         "io/ioutil"
14         "log"
15         "net"
16         "net/http"
17         "os"
18         "strings"
19         "testing"
20         "time"
21
22         "git.arvados.org/arvados.git/sdk/go/arvadosclient"
23         "git.arvados.org/arvados.git/sdk/go/arvadostest"
24         . "gopkg.in/check.v1"
25 )
26
27 // Gocheck boilerplate
28 func Test(t *testing.T) {
29         TestingT(t)
30 }
31
32 // Gocheck boilerplate
33 var _ = Suite(&ServerRequiredSuite{})
34 var _ = Suite(&StandaloneSuite{})
35
36 // Tests that require the Keep server running
37 type ServerRequiredSuite struct{}
38
39 // Standalone tests
40 type StandaloneSuite struct{}
41
42 func (s *StandaloneSuite) SetUpTest(c *C) {
43         RefreshServiceDiscovery()
44 }
45
46 func pythonDir() string {
47         cwd, _ := os.Getwd()
48         return fmt.Sprintf("%s/../../python/tests", cwd)
49 }
50
51 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
52         arvadostest.StartAPI()
53         arvadostest.StartKeep(2, false)
54 }
55
56 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
57         arvadostest.StopKeep(2)
58         arvadostest.StopAPI()
59 }
60
61 func (s *ServerRequiredSuite) SetUpTest(c *C) {
62         RefreshServiceDiscovery()
63 }
64
65 func (s *ServerRequiredSuite) TestMakeKeepClient(c *C) {
66         arv, err := arvadosclient.MakeArvadosClient()
67         c.Assert(err, Equals, nil)
68
69         kc, err := MakeKeepClient(arv)
70
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+")
75         }
76 }
77
78 func (s *ServerRequiredSuite) TestDefaultReplications(c *C) {
79         arv, err := arvadosclient.MakeArvadosClient()
80         c.Assert(err, Equals, nil)
81
82         kc, err := MakeKeepClient(arv)
83         c.Check(err, IsNil)
84         c.Assert(kc.Want_replicas, Equals, 2)
85
86         arv.DiscoveryDoc["defaultCollectionReplication"] = 3.0
87         kc, err = MakeKeepClient(arv)
88         c.Check(err, IsNil)
89         c.Assert(kc.Want_replicas, Equals, 3)
90
91         arv.DiscoveryDoc["defaultCollectionReplication"] = 1.0
92         kc, err = MakeKeepClient(arv)
93         c.Check(err, IsNil)
94         c.Assert(kc.Want_replicas, Equals, 1)
95 }
96
97 type StubPutHandler struct {
98         c                  *C
99         expectPath         string
100         expectApiToken     string
101         expectBody         string
102         expectStorageClass string
103         handled            chan string
104 }
105
106 func (sph StubPutHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
107         sph.c.Check(req.URL.Path, Equals, "/"+sph.expectPath)
108         sph.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sph.expectApiToken))
109         sph.c.Check(req.Header.Get("X-Keep-Storage-Classes"), Equals, sph.expectStorageClass)
110         body, err := ioutil.ReadAll(req.Body)
111         sph.c.Check(err, Equals, nil)
112         sph.c.Check(body, DeepEquals, []byte(sph.expectBody))
113         resp.WriteHeader(200)
114         sph.handled <- fmt.Sprintf("http://%s", req.Host)
115 }
116
117 func RunFakeKeepServer(st http.Handler) (ks KeepServer) {
118         var err error
119         // If we don't explicitly bind it to localhost, ks.listener.Addr() will
120         // bind to 0.0.0.0 or [::] which is not a valid address for Dial()
121         ks.listener, err = net.ListenTCP("tcp", &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0})
122         if err != nil {
123                 panic(fmt.Sprintf("Could not listen on any port"))
124         }
125         ks.url = fmt.Sprintf("http://%s", ks.listener.Addr().String())
126         go http.Serve(ks.listener, st)
127         return
128 }
129
130 func UploadToStubHelper(c *C, st http.Handler, f func(*KeepClient, string,
131         io.ReadCloser, io.WriteCloser, chan uploadStatus)) {
132
133         ks := RunFakeKeepServer(st)
134         defer ks.listener.Close()
135
136         arv, _ := arvadosclient.MakeArvadosClient()
137         arv.ApiToken = "abc123"
138
139         kc, _ := MakeKeepClient(arv)
140
141         reader, writer := io.Pipe()
142         upload_status := make(chan uploadStatus)
143
144         f(kc, ks.url, reader, writer, upload_status)
145 }
146
147 func (s *StandaloneSuite) TestUploadToStubKeepServer(c *C) {
148         log.Printf("TestUploadToStubKeepServer")
149
150         st := StubPutHandler{
151                 c,
152                 "acbd18db4cc2f85cedef654fccc4a4d8",
153                 "abc123",
154                 "foo",
155                 "hot",
156                 make(chan string)}
157
158         UploadToStubHelper(c, st,
159                 func(kc *KeepClient, url string, reader io.ReadCloser, writer io.WriteCloser, upload_status chan uploadStatus) {
160                         kc.StorageClasses = []string{"hot"}
161                         go kc.uploadToKeepServer(url, st.expectPath, reader, upload_status, int64(len("foo")), kc.getRequestID())
162
163                         writer.Write([]byte("foo"))
164                         writer.Close()
165
166                         <-st.handled
167                         status := <-upload_status
168                         c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
169                 })
170 }
171
172 func (s *StandaloneSuite) TestUploadToStubKeepServerBufferReader(c *C) {
173         st := StubPutHandler{
174                 c,
175                 "acbd18db4cc2f85cedef654fccc4a4d8",
176                 "abc123",
177                 "foo",
178                 "",
179                 make(chan string)}
180
181         UploadToStubHelper(c, st,
182                 func(kc *KeepClient, url string, _ io.ReadCloser, _ io.WriteCloser, upload_status chan uploadStatus) {
183                         go kc.uploadToKeepServer(url, st.expectPath, bytes.NewBuffer([]byte("foo")), upload_status, 3, kc.getRequestID())
184
185                         <-st.handled
186
187                         status := <-upload_status
188                         c.Check(status, DeepEquals, uploadStatus{nil, fmt.Sprintf("%s/%s", url, st.expectPath), 200, 1, ""})
189                 })
190 }
191
192 type FailHandler struct {
193         handled chan string
194 }
195
196 func (fh FailHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
197         resp.WriteHeader(500)
198         fh.handled <- fmt.Sprintf("http://%s", req.Host)
199 }
200
201 type FailThenSucceedHandler struct {
202         handled        chan string
203         count          int
204         successhandler http.Handler
205         reqIDs         []string
206 }
207
208 func (fh *FailThenSucceedHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
209         fh.reqIDs = append(fh.reqIDs, req.Header.Get("X-Request-Id"))
210         if fh.count == 0 {
211                 resp.WriteHeader(500)
212                 fh.count += 1
213                 fh.handled <- fmt.Sprintf("http://%s", req.Host)
214         } else {
215                 fh.successhandler.ServeHTTP(resp, req)
216         }
217 }
218
219 type Error404Handler struct {
220         handled chan string
221 }
222
223 func (fh Error404Handler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
224         resp.WriteHeader(404)
225         fh.handled <- fmt.Sprintf("http://%s", req.Host)
226 }
227
228 func (s *StandaloneSuite) TestFailedUploadToStubKeepServer(c *C) {
229         st := FailHandler{
230                 make(chan string)}
231
232         hash := "acbd18db4cc2f85cedef654fccc4a4d8"
233
234         UploadToStubHelper(c, st,
235                 func(kc *KeepClient, url string, reader io.ReadCloser,
236                         writer io.WriteCloser, upload_status chan uploadStatus) {
237
238                         go kc.uploadToKeepServer(url, hash, reader, upload_status, 3, kc.getRequestID())
239
240                         writer.Write([]byte("foo"))
241                         writer.Close()
242
243                         <-st.handled
244
245                         status := <-upload_status
246                         c.Check(status.url, Equals, fmt.Sprintf("%s/%s", url, hash))
247                         c.Check(status.statusCode, Equals, 500)
248                 })
249 }
250
251 type KeepServer struct {
252         listener net.Listener
253         url      string
254 }
255
256 func RunSomeFakeKeepServers(st http.Handler, n int) (ks []KeepServer) {
257         ks = make([]KeepServer, n)
258
259         for i := 0; i < n; i += 1 {
260                 ks[i] = RunFakeKeepServer(st)
261         }
262
263         return ks
264 }
265
266 func (s *StandaloneSuite) TestPutB(c *C) {
267         hash := Md5String("foo")
268
269         st := StubPutHandler{
270                 c,
271                 hash,
272                 "abc123",
273                 "foo",
274                 "",
275                 make(chan string, 5)}
276
277         arv, _ := arvadosclient.MakeArvadosClient()
278         kc, _ := MakeKeepClient(arv)
279
280         kc.Want_replicas = 2
281         arv.ApiToken = "abc123"
282         localRoots := make(map[string]string)
283         writableLocalRoots := make(map[string]string)
284
285         ks := RunSomeFakeKeepServers(st, 5)
286
287         for i, k := range ks {
288                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
289                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
290                 defer k.listener.Close()
291         }
292
293         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
294
295         kc.PutB([]byte("foo"))
296
297         shuff := NewRootSorter(
298                 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
299
300         s1 := <-st.handled
301         s2 := <-st.handled
302         c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
303                 (s1 == shuff[1] && s2 == shuff[0]),
304                 Equals,
305                 true)
306 }
307
308 func (s *StandaloneSuite) TestPutHR(c *C) {
309         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
310
311         st := StubPutHandler{
312                 c,
313                 hash,
314                 "abc123",
315                 "foo",
316                 "",
317                 make(chan string, 5)}
318
319         arv, _ := arvadosclient.MakeArvadosClient()
320         kc, _ := MakeKeepClient(arv)
321
322         kc.Want_replicas = 2
323         arv.ApiToken = "abc123"
324         localRoots := make(map[string]string)
325         writableLocalRoots := make(map[string]string)
326
327         ks := RunSomeFakeKeepServers(st, 5)
328
329         for i, k := range ks {
330                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
331                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
332                 defer k.listener.Close()
333         }
334
335         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
336
337         reader, writer := io.Pipe()
338
339         go func() {
340                 writer.Write([]byte("foo"))
341                 writer.Close()
342         }()
343
344         kc.PutHR(hash, reader, 3)
345
346         shuff := NewRootSorter(kc.LocalRoots(), hash).GetSortedRoots()
347
348         s1 := <-st.handled
349         s2 := <-st.handled
350
351         c.Check((s1 == shuff[0] && s2 == shuff[1]) ||
352                 (s1 == shuff[1] && s2 == shuff[0]),
353                 Equals,
354                 true)
355 }
356
357 func (s *StandaloneSuite) TestPutWithFail(c *C) {
358         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
359
360         st := StubPutHandler{
361                 c,
362                 hash,
363                 "abc123",
364                 "foo",
365                 "",
366                 make(chan string, 4)}
367
368         fh := FailHandler{
369                 make(chan string, 1)}
370
371         arv, err := arvadosclient.MakeArvadosClient()
372         c.Check(err, IsNil)
373         kc, _ := MakeKeepClient(arv)
374
375         kc.Want_replicas = 2
376         arv.ApiToken = "abc123"
377         localRoots := make(map[string]string)
378         writableLocalRoots := make(map[string]string)
379
380         ks1 := RunSomeFakeKeepServers(st, 4)
381         ks2 := RunSomeFakeKeepServers(fh, 1)
382
383         for i, k := range ks1 {
384                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
385                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
386                 defer k.listener.Close()
387         }
388         for i, k := range ks2 {
389                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
390                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
391                 defer k.listener.Close()
392         }
393
394         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
395
396         shuff := NewRootSorter(
397                 kc.LocalRoots(), Md5String("foo")).GetSortedRoots()
398         c.Logf("%+v", shuff)
399
400         phash, replicas, err := kc.PutB([]byte("foo"))
401
402         <-fh.handled
403
404         c.Check(err, Equals, nil)
405         c.Check(phash, Equals, "")
406         c.Check(replicas, Equals, 2)
407
408         s1 := <-st.handled
409         s2 := <-st.handled
410
411         c.Check((s1 == shuff[1] && s2 == shuff[2]) ||
412                 (s1 == shuff[2] && s2 == shuff[1]),
413                 Equals,
414                 true)
415 }
416
417 func (s *StandaloneSuite) TestPutWithTooManyFail(c *C) {
418         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
419
420         st := StubPutHandler{
421                 c,
422                 hash,
423                 "abc123",
424                 "foo",
425                 "",
426                 make(chan string, 1)}
427
428         fh := FailHandler{
429                 make(chan string, 4)}
430
431         arv, err := arvadosclient.MakeArvadosClient()
432         c.Check(err, IsNil)
433         kc, _ := MakeKeepClient(arv)
434
435         kc.Want_replicas = 2
436         kc.Retries = 0
437         arv.ApiToken = "abc123"
438         localRoots := make(map[string]string)
439         writableLocalRoots := make(map[string]string)
440
441         ks1 := RunSomeFakeKeepServers(st, 1)
442         ks2 := RunSomeFakeKeepServers(fh, 4)
443
444         for i, k := range ks1 {
445                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
446                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
447                 defer k.listener.Close()
448         }
449         for i, k := range ks2 {
450                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
451                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
452                 defer k.listener.Close()
453         }
454
455         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
456
457         _, replicas, err := kc.PutB([]byte("foo"))
458
459         c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
460         c.Check(replicas, Equals, 1)
461         c.Check(<-st.handled, Equals, ks1[0].url)
462 }
463
464 type StubGetHandler struct {
465         c              *C
466         expectPath     string
467         expectApiToken string
468         httpStatus     int
469         body           []byte
470 }
471
472 func (sgh StubGetHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
473         sgh.c.Check(req.URL.Path, Equals, "/"+sgh.expectPath)
474         sgh.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", sgh.expectApiToken))
475         resp.WriteHeader(sgh.httpStatus)
476         resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(sgh.body)))
477         resp.Write(sgh.body)
478 }
479
480 func (s *StandaloneSuite) TestGet(c *C) {
481         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
482
483         st := StubGetHandler{
484                 c,
485                 hash,
486                 "abc123",
487                 http.StatusOK,
488                 []byte("foo")}
489
490         ks := RunFakeKeepServer(st)
491         defer ks.listener.Close()
492
493         arv, err := arvadosclient.MakeArvadosClient()
494         c.Check(err, IsNil)
495         kc, _ := MakeKeepClient(arv)
496         arv.ApiToken = "abc123"
497         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
498
499         r, n, url2, err := kc.Get(hash)
500         defer r.Close()
501         c.Check(err, Equals, nil)
502         c.Check(n, Equals, int64(3))
503         c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
504
505         content, err2 := ioutil.ReadAll(r)
506         c.Check(err2, Equals, nil)
507         c.Check(content, DeepEquals, []byte("foo"))
508 }
509
510 func (s *StandaloneSuite) TestGet404(c *C) {
511         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
512
513         st := Error404Handler{make(chan string, 1)}
514
515         ks := RunFakeKeepServer(st)
516         defer ks.listener.Close()
517
518         arv, err := arvadosclient.MakeArvadosClient()
519         c.Check(err, IsNil)
520         kc, _ := MakeKeepClient(arv)
521         arv.ApiToken = "abc123"
522         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
523
524         r, n, url2, err := kc.Get(hash)
525         c.Check(err, Equals, BlockNotFound)
526         c.Check(n, Equals, int64(0))
527         c.Check(url2, Equals, "")
528         c.Check(r, Equals, nil)
529 }
530
531 func (s *StandaloneSuite) TestGetEmptyBlock(c *C) {
532         st := Error404Handler{make(chan string, 1)}
533
534         ks := RunFakeKeepServer(st)
535         defer ks.listener.Close()
536
537         arv, err := arvadosclient.MakeArvadosClient()
538         kc, _ := MakeKeepClient(arv)
539         arv.ApiToken = "abc123"
540         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
541
542         r, n, url2, err := kc.Get("d41d8cd98f00b204e9800998ecf8427e+0")
543         c.Check(err, IsNil)
544         c.Check(n, Equals, int64(0))
545         c.Check(url2, Equals, "")
546         c.Assert(r, NotNil)
547         buf, err := ioutil.ReadAll(r)
548         c.Check(err, IsNil)
549         c.Check(buf, DeepEquals, []byte{})
550 }
551
552 func (s *StandaloneSuite) TestGetFail(c *C) {
553         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
554
555         st := FailHandler{make(chan string, 1)}
556
557         ks := RunFakeKeepServer(st)
558         defer ks.listener.Close()
559
560         arv, err := arvadosclient.MakeArvadosClient()
561         c.Check(err, IsNil)
562         kc, _ := MakeKeepClient(arv)
563         arv.ApiToken = "abc123"
564         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
565         kc.Retries = 0
566
567         r, n, url2, err := kc.Get(hash)
568         errNotFound, _ := err.(*ErrNotFound)
569         c.Check(errNotFound, NotNil)
570         c.Check(strings.Contains(errNotFound.Error(), "HTTP 500"), Equals, true)
571         c.Check(errNotFound.Temporary(), Equals, true)
572         c.Check(n, Equals, int64(0))
573         c.Check(url2, Equals, "")
574         c.Check(r, Equals, nil)
575 }
576
577 func (s *StandaloneSuite) TestGetFailRetry(c *C) {
578         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
579
580         st := &FailThenSucceedHandler{
581                 handled: make(chan string, 1),
582                 successhandler: StubGetHandler{
583                         c,
584                         hash,
585                         "abc123",
586                         http.StatusOK,
587                         []byte("foo")}}
588
589         ks := RunFakeKeepServer(st)
590         defer ks.listener.Close()
591
592         arv, err := arvadosclient.MakeArvadosClient()
593         c.Check(err, IsNil)
594         kc, _ := MakeKeepClient(arv)
595         arv.ApiToken = "abc123"
596         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
597
598         r, n, url2, err := kc.Get(hash)
599         defer r.Close()
600         c.Check(err, Equals, nil)
601         c.Check(n, Equals, int64(3))
602         c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks.url, hash))
603
604         content, err2 := ioutil.ReadAll(r)
605         c.Check(err2, Equals, nil)
606         c.Check(content, DeepEquals, []byte("foo"))
607
608         c.Logf("%q", st.reqIDs)
609         c.Assert(len(st.reqIDs) > 1, Equals, true)
610         for _, reqid := range st.reqIDs {
611                 c.Check(reqid, Not(Equals), "")
612                 c.Check(reqid, Equals, st.reqIDs[0])
613         }
614 }
615
616 func (s *StandaloneSuite) TestGetNetError(c *C) {
617         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
618
619         arv, err := arvadosclient.MakeArvadosClient()
620         c.Check(err, IsNil)
621         kc, _ := MakeKeepClient(arv)
622         arv.ApiToken = "abc123"
623         kc.SetServiceRoots(map[string]string{"x": "http://localhost:62222"}, nil, nil)
624
625         r, n, url2, err := kc.Get(hash)
626         errNotFound, _ := err.(*ErrNotFound)
627         c.Check(errNotFound, NotNil)
628         c.Check(strings.Contains(errNotFound.Error(), "connection refused"), Equals, true)
629         c.Check(errNotFound.Temporary(), Equals, true)
630         c.Check(n, Equals, int64(0))
631         c.Check(url2, Equals, "")
632         c.Check(r, Equals, nil)
633 }
634
635 func (s *StandaloneSuite) TestGetWithServiceHint(c *C) {
636         uuid := "zzzzz-bi6l4-123451234512345"
637         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
638
639         // This one shouldn't be used:
640         ks0 := RunFakeKeepServer(StubGetHandler{
641                 c,
642                 "error if used",
643                 "abc123",
644                 http.StatusOK,
645                 []byte("foo")})
646         defer ks0.listener.Close()
647         // This one should be used:
648         ks := RunFakeKeepServer(StubGetHandler{
649                 c,
650                 hash + "+K@" + uuid,
651                 "abc123",
652                 http.StatusOK,
653                 []byte("foo")})
654         defer ks.listener.Close()
655
656         arv, err := arvadosclient.MakeArvadosClient()
657         c.Check(err, IsNil)
658         kc, _ := MakeKeepClient(arv)
659         arv.ApiToken = "abc123"
660         kc.SetServiceRoots(
661                 map[string]string{"x": ks0.url},
662                 nil,
663                 map[string]string{uuid: ks.url})
664
665         r, n, uri, err := kc.Get(hash + "+K@" + uuid)
666         defer r.Close()
667         c.Check(err, Equals, nil)
668         c.Check(n, Equals, int64(3))
669         c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
670
671         content, err := ioutil.ReadAll(r)
672         c.Check(err, Equals, nil)
673         c.Check(content, DeepEquals, []byte("foo"))
674 }
675
676 // Use a service hint to fetch from a local disk service, overriding
677 // rendezvous probe order.
678 func (s *StandaloneSuite) TestGetWithLocalServiceHint(c *C) {
679         uuid := "zzzzz-bi6l4-zzzzzzzzzzzzzzz"
680         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
681
682         // This one shouldn't be used, although it appears first in
683         // rendezvous probe order:
684         ks0 := RunFakeKeepServer(StubGetHandler{
685                 c,
686                 "error if used",
687                 "abc123",
688                 http.StatusOK,
689                 []byte("foo")})
690         defer ks0.listener.Close()
691         // This one should be used:
692         ks := RunFakeKeepServer(StubGetHandler{
693                 c,
694                 hash + "+K@" + uuid,
695                 "abc123",
696                 http.StatusOK,
697                 []byte("foo")})
698         defer ks.listener.Close()
699
700         arv, err := arvadosclient.MakeArvadosClient()
701         c.Check(err, IsNil)
702         kc, _ := MakeKeepClient(arv)
703         arv.ApiToken = "abc123"
704         kc.SetServiceRoots(
705                 map[string]string{
706                         "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
707                         "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
708                         "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
709                         uuid:                          ks.url},
710                 nil,
711                 map[string]string{
712                         "zzzzz-bi6l4-yyyyyyyyyyyyyyy": ks0.url,
713                         "zzzzz-bi6l4-xxxxxxxxxxxxxxx": ks0.url,
714                         "zzzzz-bi6l4-wwwwwwwwwwwwwww": ks0.url,
715                         uuid:                          ks.url},
716         )
717
718         r, n, uri, err := kc.Get(hash + "+K@" + uuid)
719         defer r.Close()
720         c.Check(err, Equals, nil)
721         c.Check(n, Equals, int64(3))
722         c.Check(uri, Equals, fmt.Sprintf("%s/%s", ks.url, hash+"+K@"+uuid))
723
724         content, err := ioutil.ReadAll(r)
725         c.Check(err, Equals, nil)
726         c.Check(content, DeepEquals, []byte("foo"))
727 }
728
729 func (s *StandaloneSuite) TestGetWithServiceHintFailoverToLocals(c *C) {
730         uuid := "zzzzz-bi6l4-123451234512345"
731         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
732
733         ksLocal := RunFakeKeepServer(StubGetHandler{
734                 c,
735                 hash + "+K@" + uuid,
736                 "abc123",
737                 http.StatusOK,
738                 []byte("foo")})
739         defer ksLocal.listener.Close()
740         ksGateway := RunFakeKeepServer(StubGetHandler{
741                 c,
742                 hash + "+K@" + uuid,
743                 "abc123",
744                 http.StatusInternalServerError,
745                 []byte("Error")})
746         defer ksGateway.listener.Close()
747
748         arv, err := arvadosclient.MakeArvadosClient()
749         c.Check(err, IsNil)
750         kc, _ := MakeKeepClient(arv)
751         arv.ApiToken = "abc123"
752         kc.SetServiceRoots(
753                 map[string]string{"zzzzz-bi6l4-keepdisk0000000": ksLocal.url},
754                 nil,
755                 map[string]string{uuid: ksGateway.url})
756
757         r, n, uri, err := kc.Get(hash + "+K@" + uuid)
758         c.Assert(err, Equals, nil)
759         defer r.Close()
760         c.Check(n, Equals, int64(3))
761         c.Check(uri, Equals, fmt.Sprintf("%s/%s", ksLocal.url, hash+"+K@"+uuid))
762
763         content, err := ioutil.ReadAll(r)
764         c.Check(err, Equals, nil)
765         c.Check(content, DeepEquals, []byte("foo"))
766 }
767
768 type BarHandler struct {
769         handled chan string
770 }
771
772 func (this BarHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
773         resp.Write([]byte("bar"))
774         this.handled <- fmt.Sprintf("http://%s", req.Host)
775 }
776
777 func (s *StandaloneSuite) TestChecksum(c *C) {
778         foohash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
779         barhash := fmt.Sprintf("%x", md5.Sum([]byte("bar")))
780
781         st := BarHandler{make(chan string, 1)}
782
783         ks := RunFakeKeepServer(st)
784         defer ks.listener.Close()
785
786         arv, err := arvadosclient.MakeArvadosClient()
787         c.Check(err, IsNil)
788         kc, _ := MakeKeepClient(arv)
789         arv.ApiToken = "abc123"
790         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
791
792         r, n, _, err := kc.Get(barhash)
793         c.Check(err, IsNil)
794         _, err = ioutil.ReadAll(r)
795         c.Check(n, Equals, int64(3))
796         c.Check(err, Equals, nil)
797
798         <-st.handled
799
800         r, n, _, err = kc.Get(foohash)
801         c.Check(err, IsNil)
802         _, err = ioutil.ReadAll(r)
803         c.Check(n, Equals, int64(3))
804         c.Check(err, Equals, BadChecksum)
805
806         <-st.handled
807 }
808
809 func (s *StandaloneSuite) TestGetWithFailures(c *C) {
810         content := []byte("waz")
811         hash := fmt.Sprintf("%x", md5.Sum(content))
812
813         fh := Error404Handler{
814                 make(chan string, 4)}
815
816         st := StubGetHandler{
817                 c,
818                 hash,
819                 "abc123",
820                 http.StatusOK,
821                 content}
822
823         arv, err := arvadosclient.MakeArvadosClient()
824         c.Check(err, IsNil)
825         kc, _ := MakeKeepClient(arv)
826         arv.ApiToken = "abc123"
827         localRoots := make(map[string]string)
828         writableLocalRoots := make(map[string]string)
829
830         ks1 := RunSomeFakeKeepServers(st, 1)
831         ks2 := RunSomeFakeKeepServers(fh, 4)
832
833         for i, k := range ks1 {
834                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
835                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
836                 defer k.listener.Close()
837         }
838         for i, k := range ks2 {
839                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
840                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i+len(ks1))] = k.url
841                 defer k.listener.Close()
842         }
843
844         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
845         kc.Retries = 0
846
847         // This test works only if one of the failing services is
848         // attempted before the succeeding service. Otherwise,
849         // <-fh.handled below will just hang! (Probe order depends on
850         // the choice of block content "waz" and the UUIDs of the fake
851         // servers, so we just tried different strings until we found
852         // an example that passes this Assert.)
853         c.Assert(NewRootSorter(localRoots, hash).GetSortedRoots()[0], Not(Equals), ks1[0].url)
854
855         r, n, url2, err := kc.Get(hash)
856
857         <-fh.handled
858         c.Check(err, Equals, nil)
859         c.Check(n, Equals, int64(3))
860         c.Check(url2, Equals, fmt.Sprintf("%s/%s", ks1[0].url, hash))
861
862         read_content, err2 := ioutil.ReadAll(r)
863         c.Check(err2, Equals, nil)
864         c.Check(read_content, DeepEquals, content)
865 }
866
867 func (s *ServerRequiredSuite) TestPutGetHead(c *C) {
868         content := []byte("TestPutGetHead")
869
870         arv, err := arvadosclient.MakeArvadosClient()
871         c.Check(err, IsNil)
872         kc, err := MakeKeepClient(arv)
873         c.Assert(err, Equals, nil)
874
875         hash := fmt.Sprintf("%x", md5.Sum(content))
876
877         {
878                 n, _, err := kc.Ask(hash)
879                 c.Check(err, Equals, BlockNotFound)
880                 c.Check(n, Equals, int64(0))
881         }
882         {
883                 hash2, replicas, err := kc.PutB(content)
884                 c.Check(hash2, Matches, fmt.Sprintf(`%s\+%d\b.*`, hash, len(content)))
885                 c.Check(replicas, Equals, 2)
886                 c.Check(err, Equals, nil)
887         }
888         {
889                 r, n, url2, err := kc.Get(hash)
890                 c.Check(err, Equals, nil)
891                 c.Check(n, Equals, int64(len(content)))
892                 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
893
894                 read_content, err2 := ioutil.ReadAll(r)
895                 c.Check(err2, Equals, nil)
896                 c.Check(read_content, DeepEquals, content)
897         }
898         {
899                 n, url2, err := kc.Ask(hash)
900                 c.Check(err, Equals, nil)
901                 c.Check(n, Equals, int64(len(content)))
902                 c.Check(url2, Matches, fmt.Sprintf("http://localhost:\\d+/%s", hash))
903         }
904         {
905                 loc, err := kc.LocalLocator(hash)
906                 c.Check(err, Equals, nil)
907                 c.Assert(len(loc) >= 32, Equals, true)
908                 c.Check(loc[:32], Equals, hash[:32])
909         }
910         {
911                 content := []byte("the perth county conspiracy")
912                 loc, err := kc.LocalLocator(fmt.Sprintf("%x+%d+Rzaaaa-abcde@12345", md5.Sum(content), len(content)))
913                 c.Check(loc, Equals, "")
914                 c.Check(err, ErrorMatches, `.*HEAD .*\+R.*`)
915                 c.Check(err, ErrorMatches, `.*HTTP 400.*`)
916         }
917 }
918
919 type StubProxyHandler struct {
920         handled chan string
921 }
922
923 func (this StubProxyHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
924         resp.Header().Set("X-Keep-Replicas-Stored", "2")
925         this.handled <- fmt.Sprintf("http://%s", req.Host)
926 }
927
928 func (s *StandaloneSuite) TestPutProxy(c *C) {
929         st := StubProxyHandler{make(chan string, 1)}
930
931         arv, err := arvadosclient.MakeArvadosClient()
932         c.Check(err, IsNil)
933         kc, _ := MakeKeepClient(arv)
934
935         kc.Want_replicas = 2
936         arv.ApiToken = "abc123"
937         localRoots := make(map[string]string)
938         writableLocalRoots := make(map[string]string)
939
940         ks1 := RunSomeFakeKeepServers(st, 1)
941
942         for i, k := range ks1 {
943                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
944                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
945                 defer k.listener.Close()
946         }
947
948         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
949
950         _, replicas, err := kc.PutB([]byte("foo"))
951         <-st.handled
952
953         c.Check(err, Equals, nil)
954         c.Check(replicas, Equals, 2)
955 }
956
957 func (s *StandaloneSuite) TestPutProxyInsufficientReplicas(c *C) {
958         st := StubProxyHandler{make(chan string, 1)}
959
960         arv, err := arvadosclient.MakeArvadosClient()
961         c.Check(err, IsNil)
962         kc, _ := MakeKeepClient(arv)
963
964         kc.Want_replicas = 3
965         arv.ApiToken = "abc123"
966         localRoots := make(map[string]string)
967         writableLocalRoots := make(map[string]string)
968
969         ks1 := RunSomeFakeKeepServers(st, 1)
970
971         for i, k := range ks1 {
972                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
973                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
974                 defer k.listener.Close()
975         }
976         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
977
978         _, replicas, err := kc.PutB([]byte("foo"))
979         <-st.handled
980
981         c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
982         c.Check(replicas, Equals, 2)
983 }
984
985 func (s *StandaloneSuite) TestMakeLocator(c *C) {
986         l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+3+Aabcde@12345678")
987         c.Check(err, Equals, nil)
988         c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
989         c.Check(l.Size, Equals, 3)
990         c.Check(l.Hints, DeepEquals, []string{"3", "Aabcde@12345678"})
991 }
992
993 func (s *StandaloneSuite) TestMakeLocatorNoHints(c *C) {
994         l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce")
995         c.Check(err, Equals, nil)
996         c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
997         c.Check(l.Size, Equals, -1)
998         c.Check(l.Hints, DeepEquals, []string{})
999 }
1000
1001 func (s *StandaloneSuite) TestMakeLocatorNoSizeHint(c *C) {
1002         l, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31ce+Aabcde@12345678")
1003         c.Check(err, Equals, nil)
1004         c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1005         c.Check(l.Size, Equals, -1)
1006         c.Check(l.Hints, DeepEquals, []string{"Aabcde@12345678"})
1007 }
1008
1009 func (s *StandaloneSuite) TestMakeLocatorPreservesUnrecognizedHints(c *C) {
1010         str := "91f372a266fe2bf2823cb8ec7fda31ce+3+Unknown+Kzzzzz+Afoobar"
1011         l, err := MakeLocator(str)
1012         c.Check(err, Equals, nil)
1013         c.Check(l.Hash, Equals, "91f372a266fe2bf2823cb8ec7fda31ce")
1014         c.Check(l.Size, Equals, 3)
1015         c.Check(l.Hints, DeepEquals, []string{"3", "Unknown", "Kzzzzz", "Afoobar"})
1016         c.Check(l.String(), Equals, str)
1017 }
1018
1019 func (s *StandaloneSuite) TestMakeLocatorInvalidInput(c *C) {
1020         _, err := MakeLocator("91f372a266fe2bf2823cb8ec7fda31c")
1021         c.Check(err, Equals, InvalidLocatorError)
1022 }
1023
1024 func (s *StandaloneSuite) TestPutBWant2ReplicasWithOnlyOneWritableLocalRoot(c *C) {
1025         hash := Md5String("foo")
1026
1027         st := StubPutHandler{
1028                 c,
1029                 hash,
1030                 "abc123",
1031                 "foo",
1032                 "",
1033                 make(chan string, 5)}
1034
1035         arv, _ := arvadosclient.MakeArvadosClient()
1036         kc, _ := MakeKeepClient(arv)
1037
1038         kc.Want_replicas = 2
1039         arv.ApiToken = "abc123"
1040         localRoots := make(map[string]string)
1041         writableLocalRoots := make(map[string]string)
1042
1043         ks := RunSomeFakeKeepServers(st, 5)
1044
1045         for i, k := range ks {
1046                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1047                 if i == 0 {
1048                         writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1049                 }
1050                 defer k.listener.Close()
1051         }
1052
1053         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1054
1055         _, replicas, err := kc.PutB([]byte("foo"))
1056
1057         c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1058         c.Check(replicas, Equals, 1)
1059
1060         c.Check(<-st.handled, Equals, localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", 0)])
1061 }
1062
1063 func (s *StandaloneSuite) TestPutBWithNoWritableLocalRoots(c *C) {
1064         hash := Md5String("foo")
1065
1066         st := StubPutHandler{
1067                 c,
1068                 hash,
1069                 "abc123",
1070                 "foo",
1071                 "",
1072                 make(chan string, 5)}
1073
1074         arv, _ := arvadosclient.MakeArvadosClient()
1075         kc, _ := MakeKeepClient(arv)
1076
1077         kc.Want_replicas = 2
1078         arv.ApiToken = "abc123"
1079         localRoots := make(map[string]string)
1080         writableLocalRoots := make(map[string]string)
1081
1082         ks := RunSomeFakeKeepServers(st, 5)
1083
1084         for i, k := range ks {
1085                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1086                 defer k.listener.Close()
1087         }
1088
1089         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1090
1091         _, replicas, err := kc.PutB([]byte("foo"))
1092
1093         c.Check(err, FitsTypeOf, InsufficientReplicasError(errors.New("")))
1094         c.Check(replicas, Equals, 0)
1095 }
1096
1097 type StubGetIndexHandler struct {
1098         c              *C
1099         expectPath     string
1100         expectAPIToken string
1101         httpStatus     int
1102         body           []byte
1103 }
1104
1105 func (h StubGetIndexHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
1106         h.c.Check(req.URL.Path, Equals, h.expectPath)
1107         h.c.Check(req.Header.Get("Authorization"), Equals, fmt.Sprintf("OAuth2 %s", h.expectAPIToken))
1108         resp.WriteHeader(h.httpStatus)
1109         resp.Header().Set("Content-Length", fmt.Sprintf("%d", len(h.body)))
1110         resp.Write(h.body)
1111 }
1112
1113 func (s *StandaloneSuite) TestGetIndexWithNoPrefix(c *C) {
1114         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1115
1116         st := StubGetIndexHandler{
1117                 c,
1118                 "/index",
1119                 "abc123",
1120                 http.StatusOK,
1121                 []byte(hash + "+3 1443559274\n\n")}
1122
1123         ks := RunFakeKeepServer(st)
1124         defer ks.listener.Close()
1125
1126         arv, err := arvadosclient.MakeArvadosClient()
1127         c.Assert(err, IsNil)
1128         kc, err := MakeKeepClient(arv)
1129         c.Assert(err, IsNil)
1130         arv.ApiToken = "abc123"
1131         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1132
1133         r, err := kc.GetIndex("x", "")
1134         c.Check(err, IsNil)
1135
1136         content, err2 := ioutil.ReadAll(r)
1137         c.Check(err2, Equals, nil)
1138         c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1139 }
1140
1141 func (s *StandaloneSuite) TestGetIndexWithPrefix(c *C) {
1142         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1143
1144         st := StubGetIndexHandler{
1145                 c,
1146                 "/index/" + hash[0:3],
1147                 "abc123",
1148                 http.StatusOK,
1149                 []byte(hash + "+3 1443559274\n\n")}
1150
1151         ks := RunFakeKeepServer(st)
1152         defer ks.listener.Close()
1153
1154         arv, err := arvadosclient.MakeArvadosClient()
1155         c.Check(err, IsNil)
1156         kc, _ := MakeKeepClient(arv)
1157         arv.ApiToken = "abc123"
1158         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1159
1160         r, err := kc.GetIndex("x", hash[0:3])
1161         c.Assert(err, Equals, nil)
1162
1163         content, err2 := ioutil.ReadAll(r)
1164         c.Check(err2, Equals, nil)
1165         c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1166 }
1167
1168 func (s *StandaloneSuite) TestGetIndexIncomplete(c *C) {
1169         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1170
1171         st := StubGetIndexHandler{
1172                 c,
1173                 "/index/" + hash[0:3],
1174                 "abc123",
1175                 http.StatusOK,
1176                 []byte(hash)}
1177
1178         ks := RunFakeKeepServer(st)
1179         defer ks.listener.Close()
1180
1181         arv, err := arvadosclient.MakeArvadosClient()
1182         c.Check(err, IsNil)
1183         kc, _ := MakeKeepClient(arv)
1184         arv.ApiToken = "abc123"
1185         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1186
1187         _, err = kc.GetIndex("x", hash[0:3])
1188         c.Check(err, Equals, ErrIncompleteIndex)
1189 }
1190
1191 func (s *StandaloneSuite) TestGetIndexWithNoSuchServer(c *C) {
1192         hash := fmt.Sprintf("%x", md5.Sum([]byte("foo")))
1193
1194         st := StubGetIndexHandler{
1195                 c,
1196                 "/index/" + hash[0:3],
1197                 "abc123",
1198                 http.StatusOK,
1199                 []byte(hash)}
1200
1201         ks := RunFakeKeepServer(st)
1202         defer ks.listener.Close()
1203
1204         arv, err := arvadosclient.MakeArvadosClient()
1205         c.Check(err, IsNil)
1206         kc, _ := MakeKeepClient(arv)
1207         arv.ApiToken = "abc123"
1208         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1209
1210         _, err = kc.GetIndex("y", hash[0:3])
1211         c.Check(err, Equals, ErrNoSuchKeepServer)
1212 }
1213
1214 func (s *StandaloneSuite) TestGetIndexWithNoSuchPrefix(c *C) {
1215         st := StubGetIndexHandler{
1216                 c,
1217                 "/index/abcd",
1218                 "abc123",
1219                 http.StatusOK,
1220                 []byte("\n")}
1221
1222         ks := RunFakeKeepServer(st)
1223         defer ks.listener.Close()
1224
1225         arv, err := arvadosclient.MakeArvadosClient()
1226         c.Check(err, IsNil)
1227         kc, _ := MakeKeepClient(arv)
1228         arv.ApiToken = "abc123"
1229         kc.SetServiceRoots(map[string]string{"x": ks.url}, nil, nil)
1230
1231         r, err := kc.GetIndex("x", "abcd")
1232         c.Check(err, Equals, nil)
1233
1234         content, err2 := ioutil.ReadAll(r)
1235         c.Check(err2, Equals, nil)
1236         c.Check(content, DeepEquals, st.body[0:len(st.body)-1])
1237 }
1238
1239 func (s *StandaloneSuite) TestPutBRetry(c *C) {
1240         st := &FailThenSucceedHandler{
1241                 handled: make(chan string, 1),
1242                 successhandler: StubPutHandler{
1243                         c,
1244                         Md5String("foo"),
1245                         "abc123",
1246                         "foo",
1247                         "",
1248                         make(chan string, 5)}}
1249
1250         arv, _ := arvadosclient.MakeArvadosClient()
1251         kc, _ := MakeKeepClient(arv)
1252
1253         kc.Want_replicas = 2
1254         arv.ApiToken = "abc123"
1255         localRoots := make(map[string]string)
1256         writableLocalRoots := make(map[string]string)
1257
1258         ks := RunSomeFakeKeepServers(st, 2)
1259
1260         for i, k := range ks {
1261                 localRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1262                 writableLocalRoots[fmt.Sprintf("zzzzz-bi6l4-fakefakefake%03d", i)] = k.url
1263                 defer k.listener.Close()
1264         }
1265
1266         kc.SetServiceRoots(localRoots, writableLocalRoots, nil)
1267
1268         hash, replicas, err := kc.PutB([]byte("foo"))
1269
1270         c.Check(err, Equals, nil)
1271         c.Check(hash, Equals, "")
1272         c.Check(replicas, Equals, 2)
1273 }
1274
1275 func (s *ServerRequiredSuite) TestMakeKeepClientWithNonDiskTypeService(c *C) {
1276         arv, err := arvadosclient.MakeArvadosClient()
1277         c.Assert(err, Equals, nil)
1278
1279         // Add an additional "testblobstore" keepservice
1280         blobKeepService := make(arvadosclient.Dict)
1281         err = arv.Create("keep_services",
1282                 arvadosclient.Dict{"keep_service": arvadosclient.Dict{
1283                         "service_host": "localhost",
1284                         "service_port": "21321",
1285                         "service_type": "testblobstore"}},
1286                 &blobKeepService)
1287         c.Assert(err, Equals, nil)
1288         defer func() { arv.Delete("keep_services", blobKeepService["uuid"].(string), nil, nil) }()
1289         RefreshServiceDiscovery()
1290
1291         // Make a keepclient and ensure that the testblobstore is included
1292         kc, err := MakeKeepClient(arv)
1293         c.Assert(err, Equals, nil)
1294
1295         // verify kc.LocalRoots
1296         c.Check(len(kc.LocalRoots()), Equals, 3)
1297         for _, root := range kc.LocalRoots() {
1298                 c.Check(root, Matches, "http://localhost:\\d+")
1299         }
1300         c.Assert(kc.LocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1301
1302         // verify kc.GatewayRoots
1303         c.Check(len(kc.GatewayRoots()), Equals, 3)
1304         for _, root := range kc.GatewayRoots() {
1305                 c.Check(root, Matches, "http://localhost:\\d+")
1306         }
1307         c.Assert(kc.GatewayRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1308
1309         // verify kc.WritableLocalRoots
1310         c.Check(len(kc.WritableLocalRoots()), Equals, 3)
1311         for _, root := range kc.WritableLocalRoots() {
1312                 c.Check(root, Matches, "http://localhost:\\d+")
1313         }
1314         c.Assert(kc.WritableLocalRoots()[blobKeepService["uuid"].(string)], Not(Equals), "")
1315
1316         c.Assert(kc.replicasPerService, Equals, 0)
1317         c.Assert(kc.foundNonDiskSvc, Equals, true)
1318         c.Assert(kc.httpClient().(*http.Client).Timeout, Equals, 300*time.Second)
1319 }