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