2798: Started adding tests that ShuffledServiceRoots behavior is consistent
[arvados.git] / sdk / go / src / arvados.org / keepclient / keepclient.go
1 package keepclient
2
3 import (
4         "crypto/tls"
5         "encoding/json"
6         "fmt"
7         "net/http"
8         "sort"
9         "strconv"
10 )
11
12 type KeepDisk struct {
13         Hostname string `json:"service_host"`
14         Port     int    `json:"service_port"`
15         SSL      bool   `json:"service_ssl_flag"`
16 }
17
18 func KeepDisks() (service_roots []string, err error) {
19         tr := &http.Transport{
20                 TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
21         }
22         client := &http.Client{Transport: tr}
23
24         var req *http.Request
25         if req, err = http.NewRequest("GET", "https://localhost:3001/arvados/v1/keep_disks", nil); err != nil {
26                 return nil, err
27         }
28
29         var resp *http.Response
30         req.Header.Add("Authorization", "OAuth2 4axaw8zxe0qm22wa6urpp5nskcne8z88cvbupv653y1njyi05h")
31         if resp, err = client.Do(req); err != nil {
32                 return nil, err
33         }
34
35         type SvcList struct {
36                 Items []KeepDisk `json:"items"`
37         }
38         dec := json.NewDecoder(resp.Body)
39         var m SvcList
40         if err := dec.Decode(&m); err != nil {
41                 return nil, err
42         }
43
44         service_roots = make([]string, len(m.Items))
45         for index, element := range m.Items {
46                 n := ""
47                 if element.SSL {
48                         n = "s"
49                 }
50                 service_roots[index] = fmt.Sprintf("http%s://%s:%d",
51                         n, element.Hostname, element.Port)
52         }
53         sort.Strings(service_roots)
54         return service_roots, nil
55 }
56
57 func ShuffledServiceRoots(service_roots []string, hash string) (pseq []string) {
58         // Build an ordering with which to query the Keep servers based on the
59         // contents of the hash.  "hash" is a hex-encoded number at least 8
60         // digits (32 bits) long
61
62         // seed used to calculate the next keep server from 'pool' to be added
63         // to 'pseq'
64         seed := hash
65
66         // Keep servers still to be added to the ordering
67         pool := service_roots[:]
68
69         // output probe sequence
70         pseq = make([]string, 0, len(service_roots))
71
72         // iterate while there are servers left to be assigned
73         for len(pool) > 0 {
74
75                 if len(seed) < 8 {
76                         // ran out of digits in the seed
77                         if len(pseq) < (len(hash) / 4) {
78                                 // the number of servers added to the probe
79                                 // sequence is less than the number of 4-digit
80                                 // slices in 'hash' so refill the seed with the
81                                 // last 4 digits.
82                                 seed = hash[len(hash)-4:]
83                         }
84                         seed += hash
85                 }
86
87                 // Take the next 8 digits (32 bytes) and interpret as an integer,
88                 // then modulus with the size of the remaining pool to get the next
89                 // selected server.
90                 probe, _ := strconv.ParseInt(seed[0:8], 16, 32)
91                 probe %= int64(len(pool))
92
93                 // Append the selected server to the probe sequence and remove it
94                 // from the pool.
95                 pseq = append(pseq, pool[probe])
96                 pool = append(pool[:probe], pool[probe+1:]...)
97
98                 // Remove the digits just used from the seed
99                 seed = seed[8:]
100         }
101         return pseq
102 }