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