7167: honor blob signing key while getting blocks.
[arvados.git] / tools / keep-rsync / keep-rsync_test.go
1 package main
2
3 import (
4         "crypto/md5"
5         "fmt"
6         "io/ioutil"
7         "os"
8         "testing"
9         "time"
10
11         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
12         "git.curoverse.com/arvados.git/sdk/go/keepclient"
13
14         . "gopkg.in/check.v1"
15 )
16
17 // Gocheck boilerplate
18 func Test(t *testing.T) {
19         TestingT(t)
20 }
21
22 // Gocheck boilerplate
23 var _ = Suite(&ServerRequiredSuite{})
24
25 // Tests that require the Keep server running
26 type ServerRequiredSuite struct{}
27
28 func (s *ServerRequiredSuite) SetUpSuite(c *C) {
29 }
30
31 func (s *ServerRequiredSuite) SetUpTest(c *C) {
32         arvadostest.ResetEnv()
33         srcKeepServicesJSON = ""
34         dstKeepServicesJSON = ""
35 }
36
37 func (s *ServerRequiredSuite) TearDownSuite(c *C) {
38         arvadostest.StopKeep()
39         arvadostest.StopAPI()
40 }
41
42 // Testing keep-rsync needs two sets of keep services: src and dst.
43 // The test setup hence tweaks keep-rsync initialization to achieve this.
44 // First invoke initializeKeepRsync and then invoke StartKeepWithParams
45 // to create the keep servers to be used as destination.
46 func setupRsync(c *C, enforcePermissions bool) {
47         // srcConfig
48         srcConfig.APIHost = os.Getenv("ARVADOS_API_HOST")
49         srcConfig.APIToken = os.Getenv("ARVADOS_API_TOKEN")
50         srcConfig.APIHostInsecure = matchTrue.MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE"))
51
52         // dstConfig
53         dstConfig.APIHost = os.Getenv("ARVADOS_API_HOST")
54         dstConfig.APIToken = os.Getenv("ARVADOS_API_TOKEN")
55         dstConfig.APIHostInsecure = matchTrue.MatchString(os.Getenv("ARVADOS_API_HOST_INSECURE"))
56
57         replications = 1
58         if enforcePermissions {
59                 blobSigningKey = "zfhgfenhffzltr9dixws36j1yhksjoll2grmku38mi7yxd66h5j4q9w4jzanezacp8s6q0ro3hxakfye02152hncy6zml2ed0uc"
60         }
61
62         // Start API and Keep servers
63         arvadostest.StartAPI()
64         arvadostest.StartKeepWithParams(false, enforcePermissions)
65
66         // initialize keep-rsync
67         err := initializeKeepRsync()
68         c.Assert(err, Equals, nil)
69
70         // Create two more keep servers to be used as destination
71         arvadostest.StartKeepWithParams(true, enforcePermissions)
72
73         // load kcDst
74         kcDst, err = keepclient.MakeKeepClient(&arvDst)
75         c.Assert(err, Equals, nil)
76         kcDst.Want_replicas = 1
77 }
78
79 // Test readConfigFromFile method
80 func (s *ServerRequiredSuite) TestReadConfigFromFile(c *C) {
81         // Setup a test config file
82         file, err := ioutil.TempFile(os.TempDir(), "config")
83         c.Assert(err, Equals, nil)
84         defer os.Remove(file.Name())
85
86         fileContent := "ARVADOS_API_HOST=testhost\n"
87         fileContent += "ARVADOS_API_TOKEN=testtoken\n"
88         fileContent += "ARVADOS_API_HOST_INSECURE=true\n"
89         fileContent += "ARVADOS_BLOB_SIGNING_KEY=abcdefg"
90
91         _, err = file.Write([]byte(fileContent))
92
93         // Invoke readConfigFromFile method with this test filename
94         config, err := readConfigFromFile(file.Name())
95         c.Assert(err, Equals, nil)
96         c.Assert(config.APIHost, Equals, "testhost")
97         c.Assert(config.APIToken, Equals, "testtoken")
98         c.Assert(config.APIHostInsecure, Equals, true)
99         c.Assert(config.ExternalClient, Equals, false)
100         c.Assert(blobSigningKey, Equals, "abcdefg")
101 }
102
103 // Test keep-rsync initialization, with src and dst keep servers.
104 // Do a Put and Get in src, both of which should succeed.
105 // Do a Put and Get in dst, both of which should succeed.
106 // Do a Get in dst for the src hash, which should raise block not found error.
107 // Do a Get in src for the dst hash, which should raise block not found error.
108 func (s *ServerRequiredSuite) TestRsyncPutInOne_GetFromOtherShouldFail(c *C) {
109         setupRsync(c, false)
110
111         // Put a block in src using kcSrc and Get it
112         srcData := []byte("test-data1")
113         locatorInSrc := fmt.Sprintf("%x", md5.Sum(srcData))
114
115         hash, rep, err := kcSrc.PutB(srcData)
116         c.Check(hash, Matches, fmt.Sprintf(`^%s\+10(\+.+)?$`, locatorInSrc))
117         c.Check(rep, Equals, 2)
118         c.Check(err, Equals, nil)
119
120         reader, blocklen, _, err := kcSrc.Get(locatorInSrc)
121         c.Assert(err, Equals, nil)
122         c.Check(blocklen, Equals, int64(10))
123         all, err := ioutil.ReadAll(reader)
124         c.Check(all, DeepEquals, srcData)
125
126         // Put a different block in src using kcSrc and Get it
127         dstData := []byte("test-data2")
128         locatorInDst := fmt.Sprintf("%x", md5.Sum(dstData))
129
130         hash, rep, err = kcDst.PutB(dstData)
131         c.Check(hash, Matches, fmt.Sprintf(`^%s\+10(\+.+)?$`, locatorInDst))
132         c.Check(rep, Equals, 1)
133         c.Check(err, Equals, nil)
134
135         reader, blocklen, _, err = kcDst.Get(locatorInDst)
136         c.Assert(err, Equals, nil)
137         c.Check(blocklen, Equals, int64(10))
138         all, err = ioutil.ReadAll(reader)
139         c.Check(all, DeepEquals, dstData)
140
141         // Get srcLocator using kcDst should fail with Not Found error
142         _, _, _, err = kcDst.Get(locatorInSrc)
143         c.Assert(err.Error(), Equals, "Block not found")
144
145         // Get dstLocator using kcSrc should fail with Not Found error
146         _, _, _, err = kcSrc.Get(locatorInDst)
147         c.Assert(err.Error(), Equals, "Block not found")
148 }
149
150 // Test keep-rsync initialization, with srcKeepServicesJSON
151 func (s *ServerRequiredSuite) TestRsyncInitializeWithKeepServicesJSON(c *C) {
152         srcKeepServicesJSON = "{ \"kind\":\"arvados#keepServiceList\", \"etag\":\"\", \"self_link\":\"\", \"offset\":null, \"limit\":null, \"items\":[ { \"href\":\"/keep_services/zzzzz-bi6l4-123456789012340\", \"kind\":\"arvados#keepService\", \"etag\":\"641234567890enhj7hzx432e5\", \"uuid\":\"zzzzz-bi6l4-123456789012340\", \"owner_uuid\":\"zzzzz-tpzed-123456789012345\", \"service_host\":\"keep0.zzzzz.arvadosapi.com\", \"service_port\":25107, \"service_ssl_flag\":false, \"service_type\":\"disk\", \"read_only\":false }, { \"href\":\"/keep_services/zzzzz-bi6l4-123456789012341\", \"kind\":\"arvados#keepService\", \"etag\":\"641234567890enhj7hzx432e5\", \"uuid\":\"zzzzz-bi6l4-123456789012341\", \"owner_uuid\":\"zzzzz-tpzed-123456789012345\", \"service_host\":\"keep0.zzzzz.arvadosapi.com\", \"service_port\":25108, \"service_ssl_flag\":false, \"service_type\":\"disk\", \"read_only\":false } ], \"items_available\":2 }"
153
154         setupRsync(c, false)
155
156         localRoots := kcSrc.LocalRoots()
157         c.Check(localRoots != nil, Equals, true)
158
159         foundIt := false
160         for k := range localRoots {
161                 if k == "zzzzz-bi6l4-123456789012340" {
162                         foundIt = true
163                 }
164         }
165         c.Check(foundIt, Equals, true)
166
167         foundIt = false
168         for k := range localRoots {
169                 if k == "zzzzz-bi6l4-123456789012341" {
170                         foundIt = true
171                 }
172         }
173         c.Check(foundIt, Equals, true)
174 }
175
176 // Test keep-rsync initialization, with src and dst keep servers with blobSigningKey.
177 // Do a Put and Get in src, both of which should succeed.
178 // Do a Put and Get in dst, both of which should succeed.
179 // Do a Get in dst for the src hash, which should raise block not found error.
180 // Do a Get in src for the dst hash, which should raise block not found error.
181 func (s *ServerRequiredSuite) TestRsyncWithBlobSigning_PutInOne_GetFromOtherShouldFail(c *C) {
182         setupRsync(c, true)
183
184         // Put a block in src using kcSrc and Get it
185         srcData := []byte("test-data1")
186         locatorInSrc := fmt.Sprintf("%x", md5.Sum(srcData))
187
188         hash, rep, err := kcSrc.PutB(srcData)
189         c.Check(hash, Matches, fmt.Sprintf(`^%s\+10(\+.+)?$`, locatorInSrc))
190         c.Check(rep, Equals, 2)
191         c.Check(err, Equals, nil)
192
193         tomorrow := time.Now().AddDate(0, 0, 1)
194         signedLocator := keepclient.SignLocator(locatorInSrc, arvSrc.ApiToken, tomorrow, []byte(blobSigningKey))
195
196         reader, blocklen, _, err := kcSrc.Get(signedLocator)
197         c.Assert(err, Equals, nil)
198         c.Check(blocklen, Equals, int64(10))
199         all, err := ioutil.ReadAll(reader)
200         c.Check(all, DeepEquals, srcData)
201
202         // Put a different block in src using kcSrc and Get it
203         dstData := []byte("test-data2")
204         locatorInDst := fmt.Sprintf("%x", md5.Sum(dstData))
205
206         hash, rep, err = kcDst.PutB(dstData)
207         c.Check(hash, Matches, fmt.Sprintf(`^%s\+10(\+.+)?$`, locatorInDst))
208         c.Check(rep, Equals, 1)
209         c.Check(err, Equals, nil)
210
211         signedLocator = keepclient.SignLocator(locatorInDst, arvDst.ApiToken, tomorrow, []byte(blobSigningKey))
212
213         reader, blocklen, _, err = kcDst.Get(signedLocator)
214         c.Assert(err, Equals, nil)
215         c.Check(blocklen, Equals, int64(10))
216         all, err = ioutil.ReadAll(reader)
217         c.Check(all, DeepEquals, dstData)
218
219         // Get srcLocator using kcDst should fail with Not Found error
220         signedLocator = keepclient.SignLocator(locatorInSrc, arvDst.ApiToken, tomorrow, []byte(blobSigningKey))
221         _, _, _, err = kcDst.Get(locatorInSrc)
222         c.Assert(err.Error(), Equals, "Block not found")
223
224         // Get dstLocator using kcSrc should fail with Not Found error
225         signedLocator = keepclient.SignLocator(locatorInDst, arvSrc.ApiToken, tomorrow, []byte(blobSigningKey))
226         _, _, _, err = kcSrc.Get(locatorInDst)
227         c.Assert(err.Error(), Equals, "Block not found")
228 }
229
230 // Put some blocks in Src and some more in Dst
231 // And copy missing blocks from Src to Dst
232 func (s *ServerRequiredSuite) TestKeepRsync(c *C) {
233         testKeepRsync(c, false)
234 }
235
236 // Put some blocks in Src and some more in Dst with blob signing enabled.
237 // And copy missing blocks from Src to Dst
238 func (s *ServerRequiredSuite) TestKeepRsync_WithBlobSigning(c *C) {
239         testKeepRsync(c, true)
240 }
241
242 // Put 5 blocks in src. Put 2 of those blocks in dst
243 // Hence there are 3 additional blocks in src
244 // Also, put 2 extra blocks in dst; they are hence only in dst
245 // Run rsync and verify that those 7 blocks are now available in dst
246 func testKeepRsync(c *C, enforcePermissions bool) {
247         setupRsync(c, enforcePermissions)
248
249         tomorrow := time.Now().AddDate(0, 0, 1)
250
251         // Put a few blocks in src using kcSrc
252         var srcLocators []string
253         for i := 0; i < 5; i++ {
254                 data := []byte(fmt.Sprintf("test-data-%d", i))
255                 hash := fmt.Sprintf("%x", md5.Sum(data))
256
257                 hash2, rep, err := kcSrc.PutB(data)
258                 c.Check(hash2, Matches, fmt.Sprintf(`^%s\+11(\+.+)?$`, hash))
259                 c.Check(rep, Equals, 2)
260                 c.Check(err, Equals, nil)
261
262                 getLocator := hash
263                 if enforcePermissions {
264                         getLocator = keepclient.SignLocator(getLocator, arvSrc.ApiToken, tomorrow, []byte(blobSigningKey))
265                 }
266
267                 reader, blocklen, _, err := kcSrc.Get(getLocator)
268                 c.Assert(err, Equals, nil)
269                 c.Check(blocklen, Equals, int64(11))
270                 all, err := ioutil.ReadAll(reader)
271                 c.Check(all, DeepEquals, data)
272
273                 srcLocators = append(srcLocators, fmt.Sprintf("%s+%d", hash, blocklen))
274         }
275
276         // Put just two of those blocks in dst using kcDst
277         var dstLocators []string
278         for i := 0; i < 2; i++ {
279                 data := []byte(fmt.Sprintf("test-data-%d", i))
280                 hash := fmt.Sprintf("%x", md5.Sum(data))
281
282                 hash2, rep, err := kcDst.PutB(data)
283                 c.Check(hash2, Matches, fmt.Sprintf(`^%s\+11(\+.+)?$`, hash))
284                 c.Check(rep, Equals, 1)
285                 c.Check(err, Equals, nil)
286
287                 getLocator := hash
288                 if enforcePermissions {
289                         getLocator = keepclient.SignLocator(getLocator, arvDst.ApiToken, tomorrow, []byte(blobSigningKey))
290                 }
291
292                 reader, blocklen, _, err := kcDst.Get(getLocator)
293                 c.Assert(err, Equals, nil)
294                 c.Check(blocklen, Equals, int64(11))
295                 all, err := ioutil.ReadAll(reader)
296                 c.Check(all, DeepEquals, data)
297
298                 dstLocators = append(dstLocators, fmt.Sprintf("%s+%d", hash, blocklen))
299         }
300
301         // Put two more blocks in dst; they are not in src at all
302         var extraDstLocators []string
303         for i := 0; i < 2; i++ {
304                 data := []byte(fmt.Sprintf("other-data-%d", i))
305                 hash := fmt.Sprintf("%x", md5.Sum(data))
306
307                 hash2, rep, err := kcDst.PutB(data)
308                 c.Check(hash2, Matches, fmt.Sprintf(`^%s\+12(\+.+)?$`, hash))
309                 c.Check(rep, Equals, 1)
310                 c.Check(err, Equals, nil)
311
312                 getLocator := hash
313                 if enforcePermissions {
314                         getLocator = keepclient.SignLocator(getLocator, arvDst.ApiToken, tomorrow, []byte(blobSigningKey))
315                 }
316
317                 reader, blocklen, _, err := kcDst.Get(getLocator)
318                 c.Assert(err, Equals, nil)
319                 c.Check(blocklen, Equals, int64(12))
320                 all, err := ioutil.ReadAll(reader)
321                 c.Check(all, DeepEquals, data)
322
323                 extraDstLocators = append(extraDstLocators, fmt.Sprintf("%s+%d", hash, blocklen))
324         }
325
326         err := performKeepRsync()
327         c.Check(err, Equals, nil)
328
329         // Now GetIndex from dst and verify that all 5 from src and the 2 extra blocks are found
330         dstIndex, err := getUniqueLocators(kcDst, "")
331         c.Check(err, Equals, nil)
332         for _, locator := range srcLocators {
333                 _, ok := dstIndex[locator]
334                 c.Assert(ok, Equals, true)
335         }
336         for _, locator := range extraDstLocators {
337                 _, ok := dstIndex[locator]
338                 c.Assert(ok, Equals, true)
339         }
340 }