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