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