~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/maas/storage_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

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