~juju-qa/ubuntu/yakkety/juju/2.0-rc3-again

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/environs/maas/storage_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-04-24 22:34:47 UTC
  • Revision ID: package-import@ubuntu.com-20130424223447-f0qdji7ubnyo0s71
Tags: upstream-1.10.0.1
ImportĀ upstreamĀ versionĀ 1.10.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package maas
 
2
 
 
3
import (
 
4
        "bytes"
 
5
        "encoding/base64"
 
6
        "io/ioutil"
 
7
        . "launchpad.net/gocheck"
 
8
        "launchpad.net/gomaasapi"
 
9
        "launchpad.net/juju-core/environs"
 
10
        "math/rand"
 
11
        "net/http"
 
12
        "net/url"
 
13
        "sync"
 
14
)
 
15
 
 
16
type StorageSuite struct {
 
17
        ProviderSuite
 
18
}
 
19
 
 
20
var _ = Suite(new(StorageSuite))
 
21
 
 
22
// makeStorage creates a MAAS storage object for the running test.
 
23
func (s *StorageSuite) makeStorage(name string) *maasStorage {
 
24
        maasobj := s.testMAASObject.MAASObject
 
25
        env := maasEnviron{name: name, maasClientUnlocked: &maasobj}
 
26
        return NewStorage(&env).(*maasStorage)
 
27
}
 
28
 
 
29
// makeRandomBytes returns an array of arbitrary byte values.
 
30
func makeRandomBytes(length int) []byte {
 
31
        data := make([]byte, length)
 
32
        for index := range data {
 
33
                data[index] = byte(rand.Intn(256))
 
34
        }
 
35
        return data
 
36
}
 
37
 
 
38
// fakeStoredFile creates a file directly in the (simulated) MAAS file store.
 
39
// It will contain an arbitrary amount of random data.  The contents are also
 
40
// returned.
 
41
//
 
42
// If you want properly random data here, initialize the randomizer first.
 
43
// Or don't, if you want consistent (and debuggable) results.
 
44
func (s *StorageSuite) fakeStoredFile(storage environs.Storage, name string) gomaasapi.MAASObject {
 
45
        data := makeRandomBytes(rand.Intn(10))
 
46
        return s.testMAASObject.TestServer.NewFile(name, data)
 
47
}
 
48
 
 
49
func (s *StorageSuite) TestGetSnapshotCreatesClone(c *C) {
 
50
        original := s.makeStorage("storage-name")
 
51
        snapshot := original.getSnapshot()
 
52
        c.Check(snapshot.environUnlocked, Equals, original.environUnlocked)
 
53
        c.Check(snapshot.maasClientUnlocked.URL().String(), Equals, original.maasClientUnlocked.URL().String())
 
54
        // Snapshotting locks the original internally, but does not leave
 
55
        // either the original or the snapshot locked.
 
56
        unlockedMutexValue := sync.Mutex{}
 
57
        c.Check(original.Mutex, Equals, unlockedMutexValue)
 
58
        c.Check(snapshot.Mutex, Equals, unlockedMutexValue)
 
59
}
 
60
 
 
61
func (s *StorageSuite) TestGetRetrievesFile(c *C) {
 
62
        const filename = "stored-data"
 
63
        storage := s.makeStorage("get-retrieves-file")
 
64
        file := s.fakeStoredFile(storage, filename)
 
65
        base64Content, err := file.GetField("content")
 
66
        c.Assert(err, IsNil)
 
67
        content, err := base64.StdEncoding.DecodeString(base64Content)
 
68
        c.Assert(err, IsNil)
 
69
 
 
70
        reader, err := storage.Get(filename)
 
71
        c.Assert(err, IsNil)
 
72
        defer reader.Close()
 
73
 
 
74
        buf, err := ioutil.ReadAll(reader)
 
75
        c.Assert(err, IsNil)
 
76
        c.Check(len(buf), Equals, len(content))
 
77
        c.Check(buf, DeepEquals, content)
 
78
}
 
79
 
 
80
func (s *StorageSuite) TestRetrieveFileObjectReturnsFileObject(c *C) {
 
81
        const filename = "myfile"
 
82
        stor := s.makeStorage("rfo-test")
 
83
        file := s.fakeStoredFile(stor, filename)
 
84
        fileURI, err := file.GetField("anon_resource_uri")
 
85
        c.Assert(err, IsNil)
 
86
        fileContent, err := file.GetField("content")
 
87
        c.Assert(err, IsNil)
 
88
 
 
89
        obj, err := stor.retrieveFileObject(filename)
 
90
        c.Assert(err, IsNil)
 
91
 
 
92
        uri, err := obj.GetField("anon_resource_uri")
 
93
        c.Assert(err, IsNil)
 
94
        c.Check(uri, Equals, fileURI)
 
95
        content, err := obj.GetField("content")
 
96
        c.Check(content, Equals, fileContent)
 
97
}
 
98
 
 
99
func (s *StorageSuite) TestRetrieveFileObjectReturnsNotFoundForMissingFile(c *C) {
 
100
        stor := s.makeStorage("rfo-test")
 
101
        _, err := stor.retrieveFileObject("nonexistent-file")
 
102
        c.Assert(err, NotNil)
 
103
        c.Check(err, FitsTypeOf, &environs.NotFoundError{})
 
104
}
 
105
 
 
106
func (s *StorageSuite) TestRetrieveFileObjectEscapesName(c *C) {
 
107
        const filename = "#a?b c&d%e!"
 
108
        data := []byte("File contents here")
 
109
        stor := s.makeStorage("rfo-test")
 
110
        err := stor.Put(filename, bytes.NewReader(data), int64(len(data)))
 
111
        c.Assert(err, IsNil)
 
112
 
 
113
        obj, err := stor.retrieveFileObject(filename)
 
114
        c.Assert(err, IsNil)
 
115
 
 
116
        base64Content, err := obj.GetField("content")
 
117
        c.Assert(err, IsNil)
 
118
        content, err := base64.StdEncoding.DecodeString(base64Content)
 
119
        c.Assert(err, IsNil)
 
120
        c.Check(content, DeepEquals, data)
 
121
}
 
122
 
 
123
func (s *StorageSuite) TestFileContentsAreBinary(c *C) {
 
124
        const filename = "myfile.bin"
 
125
        data := []byte{0, 1, 255, 2, 254, 3}
 
126
        stor := s.makeStorage("binary-test")
 
127
 
 
128
        err := stor.Put(filename, bytes.NewReader(data), int64(len(data)))
 
129
        c.Assert(err, IsNil)
 
130
        file, err := stor.Get(filename)
 
131
        c.Assert(err, IsNil)
 
132
        content, err := ioutil.ReadAll(file)
 
133
        c.Assert(err, IsNil)
 
134
 
 
135
        c.Check(content, DeepEquals, data)
 
136
}
 
137
 
 
138
func (s *StorageSuite) TestGetReturnsNotFoundErrorIfNotFound(c *C) {
 
139
        const filename = "lost-data"
 
140
        storage := NewStorage(s.environ)
 
141
        _, err := storage.Get(filename)
 
142
        c.Assert(err, FitsTypeOf, &environs.NotFoundError{})
 
143
}
 
144
 
 
145
func (s *StorageSuite) TestListReturnsEmptyIfNoFilesStored(c *C) {
 
146
        storage := NewStorage(s.environ)
 
147
        listing, err := storage.List("")
 
148
        c.Assert(err, IsNil)
 
149
        c.Check(listing, DeepEquals, []string{})
 
150
}
 
151
 
 
152
func (s *StorageSuite) TestListReturnsAllFilesIfPrefixEmpty(c *C) {
 
153
        storage := NewStorage(s.environ)
 
154
        files := []string{"1a", "2b", "3c"}
 
155
        for _, name := range files {
 
156
                s.fakeStoredFile(storage, name)
 
157
        }
 
158
 
 
159
        listing, err := storage.List("")
 
160
        c.Assert(err, IsNil)
 
161
        c.Check(listing, DeepEquals, files)
 
162
}
 
163
 
 
164
func (s *StorageSuite) TestListSortsResults(c *C) {
 
165
        storage := NewStorage(s.environ)
 
166
        files := []string{"4d", "1a", "3c", "2b"}
 
167
        for _, name := range files {
 
168
                s.fakeStoredFile(storage, name)
 
169
        }
 
170
 
 
171
        listing, err := storage.List("")
 
172
        c.Assert(err, IsNil)
 
173
        c.Check(listing, DeepEquals, []string{"1a", "2b", "3c", "4d"})
 
174
}
 
175
 
 
176
func (s *StorageSuite) TestListReturnsNoFilesIfNoFilesMatchPrefix(c *C) {
 
177
        storage := NewStorage(s.environ)
 
178
        s.fakeStoredFile(storage, "foo")
 
179
 
 
180
        listing, err := storage.List("bar")
 
181
        c.Assert(err, IsNil)
 
182
        c.Check(listing, DeepEquals, []string{})
 
183
}
 
184
 
 
185
func (s *StorageSuite) TestListReturnsOnlyFilesWithMatchingPrefix(c *C) {
 
186
        storage := NewStorage(s.environ)
 
187
        s.fakeStoredFile(storage, "abc")
 
188
        s.fakeStoredFile(storage, "xyz")
 
189
 
 
190
        listing, err := storage.List("x")
 
191
        c.Assert(err, IsNil)
 
192
        c.Check(listing, DeepEquals, []string{"xyz"})
 
193
}
 
194
 
 
195
func (s *StorageSuite) TestListMatchesPrefixOnly(c *C) {
 
196
        storage := NewStorage(s.environ)
 
197
        s.fakeStoredFile(storage, "abc")
 
198
        s.fakeStoredFile(storage, "xabc")
 
199
 
 
200
        listing, err := storage.List("a")
 
201
        c.Assert(err, IsNil)
 
202
        c.Check(listing, DeepEquals, []string{"abc"})
 
203
}
 
204
 
 
205
func (s *StorageSuite) TestListOperatesOnFlatNamespace(c *C) {
 
206
        storage := NewStorage(s.environ)
 
207
        s.fakeStoredFile(storage, "a/b/c/d")
 
208
 
 
209
        listing, err := storage.List("a/b")
 
210
        c.Assert(err, IsNil)
 
211
        c.Check(listing, DeepEquals, []string{"a/b/c/d"})
 
212
}
 
213
 
 
214
// getFileAtURL requests, and returns, the file at the given URL.
 
215
func getFileAtURL(fileURL string) ([]byte, error) {
 
216
        response, err := http.Get(fileURL)
 
217
        if err != nil {
 
218
                return nil, err
 
219
        }
 
220
        body, err := ioutil.ReadAll(response.Body)
 
221
        if err != nil {
 
222
                return nil, err
 
223
        }
 
224
        return body, nil
 
225
}
 
226
 
 
227
func (s *StorageSuite) TestURLReturnsURLCorrespondingToFile(c *C) {
 
228
        const filename = "my-file.txt"
 
229
        storage := NewStorage(s.environ).(*maasStorage)
 
230
        file := s.fakeStoredFile(storage, filename)
 
231
        // The file contains an anon_resource_uri, which lacks a network part
 
232
        // (but will probably contain a query part).  anonURL will be the
 
233
        // file's full URL.
 
234
        anonURI, err := file.GetField("anon_resource_uri")
 
235
        c.Assert(err, IsNil)
 
236
        parsedURI, err := url.Parse(anonURI)
 
237
        c.Assert(err, IsNil)
 
238
        anonURL := storage.maasClientUnlocked.URL().ResolveReference(parsedURI)
 
239
        c.Assert(err, IsNil)
 
240
 
 
241
        fileURL, err := storage.URL(filename)
 
242
        c.Assert(err, IsNil)
 
243
 
 
244
        c.Check(fileURL, NotNil)
 
245
        c.Check(fileURL, Equals, anonURL.String())
 
246
}
 
247
 
 
248
func (s *StorageSuite) TestPutStoresRetrievableFile(c *C) {
 
249
        const filename = "broken-toaster.jpg"
 
250
        contents := []byte("Contents here")
 
251
        length := int64(len(contents))
 
252
        storage := NewStorage(s.environ)
 
253
 
 
254
        err := storage.Put(filename, bytes.NewReader(contents), length)
 
255
 
 
256
        reader, err := storage.Get(filename)
 
257
        c.Assert(err, IsNil)
 
258
        defer reader.Close()
 
259
 
 
260
        buf, err := ioutil.ReadAll(reader)
 
261
        c.Assert(err, IsNil)
 
262
        c.Check(buf, DeepEquals, contents)
 
263
}
 
264
 
 
265
func (s *StorageSuite) TestPutOverwritesFile(c *C) {
 
266
        const filename = "foo.bar"
 
267
        storage := NewStorage(s.environ)
 
268
        s.fakeStoredFile(storage, filename)
 
269
        newContents := []byte("Overwritten")
 
270
 
 
271
        err := storage.Put(filename, bytes.NewReader(newContents), int64(len(newContents)))
 
272
        c.Assert(err, IsNil)
 
273
 
 
274
        reader, err := storage.Get(filename)
 
275
        c.Assert(err, IsNil)
 
276
        defer reader.Close()
 
277
 
 
278
        buf, err := ioutil.ReadAll(reader)
 
279
        c.Assert(err, IsNil)
 
280
        c.Check(len(buf), Equals, len(newContents))
 
281
        c.Check(buf, DeepEquals, newContents)
 
282
}
 
283
 
 
284
func (s *StorageSuite) TestPutStopsAtGivenLength(c *C) {
 
285
        const filename = "xyzzyz.2.xls"
 
286
        const length = 5
 
287
        contents := []byte("abcdefghijklmnopqrstuvwxyz")
 
288
        storage := NewStorage(s.environ)
 
289
 
 
290
        err := storage.Put(filename, bytes.NewReader(contents), length)
 
291
        c.Assert(err, IsNil)
 
292
 
 
293
        reader, err := storage.Get(filename)
 
294
        c.Assert(err, IsNil)
 
295
        defer reader.Close()
 
296
 
 
297
        buf, err := ioutil.ReadAll(reader)
 
298
        c.Assert(err, IsNil)
 
299
        c.Check(len(buf), Equals, length)
 
300
}
 
301
 
 
302
func (s *StorageSuite) TestPutToExistingFileTruncatesAtGivenLength(c *C) {
 
303
        const filename = "a-file-which-is-mine"
 
304
        oldContents := []byte("abcdefghijklmnopqrstuvwxyz")
 
305
        newContents := []byte("xyz")
 
306
        storage := NewStorage(s.environ)
 
307
        err := storage.Put(filename, bytes.NewReader(oldContents), int64(len(oldContents)))
 
308
        c.Assert(err, IsNil)
 
309
 
 
310
        err = storage.Put(filename, bytes.NewReader(newContents), int64(len(newContents)))
 
311
        c.Assert(err, IsNil)
 
312
 
 
313
        reader, err := storage.Get(filename)
 
314
        c.Assert(err, IsNil)
 
315
        defer reader.Close()
 
316
 
 
317
        buf, err := ioutil.ReadAll(reader)
 
318
        c.Assert(err, IsNil)
 
319
        c.Check(len(buf), Equals, len(newContents))
 
320
        c.Check(buf, DeepEquals, newContents)
 
321
}
 
322
 
 
323
func (s *StorageSuite) TestRemoveDeletesFile(c *C) {
 
324
        const filename = "doomed.txt"
 
325
        storage := NewStorage(s.environ)
 
326
        s.fakeStoredFile(storage, filename)
 
327
 
 
328
        err := storage.Remove(filename)
 
329
        c.Assert(err, IsNil)
 
330
 
 
331
        _, err = storage.Get(filename)
 
332
        c.Assert(err, FitsTypeOf, &environs.NotFoundError{})
 
333
 
 
334
        listing, err := storage.List(filename)
 
335
        c.Assert(err, IsNil)
 
336
        c.Assert(listing, DeepEquals, []string{})
 
337
}
 
338
 
 
339
func (s *StorageSuite) TestRemoveIsIdempotent(c *C) {
 
340
        const filename = "half-a-file"
 
341
        storage := NewStorage(s.environ)
 
342
        s.fakeStoredFile(storage, filename)
 
343
 
 
344
        err := storage.Remove(filename)
 
345
        c.Assert(err, IsNil)
 
346
 
 
347
        err = storage.Remove(filename)
 
348
        c.Assert(err, IsNil)
 
349
}
 
350
 
 
351
func (s *StorageSuite) TestNamesMayHaveSlashes(c *C) {
 
352
        const filename = "name/with/slashes"
 
353
        content := []byte("File contents")
 
354
        storage := NewStorage(s.environ)
 
355
 
 
356
        err := storage.Put(filename, bytes.NewReader(content), int64(len(content)))
 
357
        c.Assert(err, IsNil)
 
358
 
 
359
        // There's not much we can say about the anonymous URL, except that
 
360
        // we get one.
 
361
        anonURL, err := storage.URL(filename)
 
362
        c.Assert(err, IsNil)
 
363
        c.Check(anonURL, Matches, "http[s]*://.*")
 
364
 
 
365
        reader, err := storage.Get(filename)
 
366
        c.Assert(err, IsNil)
 
367
        defer reader.Close()
 
368
        data, err := ioutil.ReadAll(reader)
 
369
        c.Assert(err, IsNil)
 
370
        c.Check(data, DeepEquals, content)
 
371
}
 
372
 
 
373
func (s *StorageSuite) TestDeleteAllDeletesAllFiles(c *C) {
 
374
        storage := s.makeStorage("get-retrieves-file")
 
375
        const filename1 = "stored-data1"
 
376
        s.fakeStoredFile(storage, filename1)
 
377
        const filename2 = "stored-data2"
 
378
        s.fakeStoredFile(storage, filename2)
 
379
 
 
380
        err := storage.deleteAll()
 
381
        c.Assert(err, IsNil)
 
382
        listing, err := storage.List("")
 
383
        c.Assert(err, IsNil)
 
384
        c.Assert(listing, DeepEquals, []string{})
 
385
}