~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/state/toolstorage/tools_test.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2014 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
 
package toolstorage_test
5
 
 
6
 
import (
7
 
        "bytes"
8
 
        "fmt"
9
 
        "io/ioutil"
10
 
        "strings"
11
 
        stdtesting "testing"
12
 
 
13
 
        "github.com/juju/errors"
14
 
        gitjujutesting "github.com/juju/testing"
15
 
        jc "github.com/juju/testing/checkers"
16
 
        jujutxn "github.com/juju/txn"
17
 
        txntesting "github.com/juju/txn/testing"
18
 
        "github.com/juju/utils/arch"
19
 
        "github.com/juju/utils/series"
20
 
        gc "gopkg.in/check.v1"
21
 
        "gopkg.in/juju/blobstore.v2"
22
 
        "gopkg.in/mgo.v2"
23
 
 
24
 
        "github.com/juju/juju/state/toolstorage"
25
 
        "github.com/juju/juju/testing"
26
 
        "github.com/juju/juju/version"
27
 
)
28
 
 
29
 
var _ = gc.Suite(&ToolsSuite{})
30
 
var current = version.Binary{
31
 
        Number: version.Current,
32
 
        Arch:   arch.HostArch(),
33
 
        Series: series.HostSeries(),
34
 
}
35
 
 
36
 
func TestPackage(t *stdtesting.T) {
37
 
        gc.TestingT(t)
38
 
}
39
 
 
40
 
type ToolsSuite struct {
41
 
        testing.BaseSuite
42
 
        mongo              *gitjujutesting.MgoInstance
43
 
        session            *mgo.Session
44
 
        storage            toolstorage.Storage
45
 
        managedStorage     blobstore.ManagedStorage
46
 
        metadataCollection *mgo.Collection
47
 
        txnRunner          jujutxn.Runner
48
 
}
49
 
 
50
 
func (s *ToolsSuite) SetUpTest(c *gc.C) {
51
 
        s.BaseSuite.SetUpTest(c)
52
 
        s.mongo = &gitjujutesting.MgoInstance{}
53
 
        s.mongo.Start(nil)
54
 
 
55
 
        var err error
56
 
        s.session, err = s.mongo.Dial()
57
 
        c.Assert(err, jc.ErrorIsNil)
58
 
        rs := blobstore.NewGridFS("blobstore", "my-uuid", s.session)
59
 
        catalogue := s.session.DB("catalogue")
60
 
        s.managedStorage = blobstore.NewManagedStorage(catalogue, rs)
61
 
        s.metadataCollection = catalogue.C("toolsmetadata")
62
 
        s.txnRunner = jujutxn.NewRunner(jujutxn.RunnerParams{Database: catalogue})
63
 
        s.storage = toolstorage.NewStorage("my-uuid", s.managedStorage, s.metadataCollection, s.txnRunner)
64
 
}
65
 
 
66
 
func (s *ToolsSuite) TearDownTest(c *gc.C) {
67
 
        s.session.Close()
68
 
        s.mongo.DestroyWithLog()
69
 
        s.BaseSuite.TearDownTest(c)
70
 
}
71
 
 
72
 
func (s *ToolsSuite) TestAddTools(c *gc.C) {
73
 
        s.testAddTools(c, "some-tools")
74
 
}
75
 
 
76
 
func (s *ToolsSuite) TestAddToolsReplaces(c *gc.C) {
77
 
        s.testAddTools(c, "abc")
78
 
        s.testAddTools(c, "def")
79
 
}
80
 
 
81
 
func (s *ToolsSuite) testAddTools(c *gc.C, content string) {
82
 
        r := bytes.NewReader([]byte(content))
83
 
        addedMetadata := toolstorage.Metadata{
84
 
                Version: current,
85
 
                Size:    int64(len(content)),
86
 
                SHA256:  "hash(" + content + ")",
87
 
        }
88
 
        err := s.storage.AddTools(r, addedMetadata)
89
 
        c.Assert(err, jc.ErrorIsNil)
90
 
 
91
 
        metadata, rc, err := s.storage.Tools(current)
92
 
        c.Assert(err, jc.ErrorIsNil)
93
 
        c.Assert(r, gc.NotNil)
94
 
        defer rc.Close()
95
 
        c.Assert(metadata, gc.Equals, addedMetadata)
96
 
 
97
 
        data, err := ioutil.ReadAll(rc)
98
 
        c.Assert(err, jc.ErrorIsNil)
99
 
        c.Assert(string(data), gc.Equals, content)
100
 
}
101
 
 
102
 
func bumpVersion(v version.Binary) version.Binary {
103
 
        v.Build++
104
 
        return v
105
 
}
106
 
 
107
 
func (s *ToolsSuite) TestAllMetadata(c *gc.C) {
108
 
        metadata, err := s.storage.AllMetadata()
109
 
        c.Assert(err, jc.ErrorIsNil)
110
 
        c.Assert(metadata, gc.HasLen, 0)
111
 
 
112
 
        s.addMetadataDoc(c, current, 3, "hash(abc)", "path")
113
 
        metadata, err = s.storage.AllMetadata()
114
 
        c.Assert(err, jc.ErrorIsNil)
115
 
        c.Assert(metadata, gc.HasLen, 1)
116
 
        expected := []toolstorage.Metadata{{
117
 
                Version: current,
118
 
                Size:    3,
119
 
                SHA256:  "hash(abc)",
120
 
        }}
121
 
        c.Assert(metadata, jc.SameContents, expected)
122
 
 
123
 
        alias := bumpVersion(current)
124
 
        s.addMetadataDoc(c, alias, 3, "hash(abc)", "path")
125
 
 
126
 
        metadata, err = s.storage.AllMetadata()
127
 
        c.Assert(err, jc.ErrorIsNil)
128
 
        c.Assert(metadata, gc.HasLen, 2)
129
 
        expected = append(expected, toolstorage.Metadata{
130
 
                Version: alias,
131
 
                Size:    3,
132
 
                SHA256:  "hash(abc)",
133
 
        })
134
 
        c.Assert(metadata, jc.SameContents, expected)
135
 
}
136
 
 
137
 
func (s *ToolsSuite) TestMetadata(c *gc.C) {
138
 
        metadata, err := s.storage.Metadata(current)
139
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
140
 
 
141
 
        s.addMetadataDoc(c, current, 3, "hash(abc)", "path")
142
 
        metadata, err = s.storage.Metadata(current)
143
 
        c.Assert(err, jc.ErrorIsNil)
144
 
        c.Assert(metadata, gc.Equals, toolstorage.Metadata{
145
 
                Version: current,
146
 
                Size:    3,
147
 
                SHA256:  "hash(abc)",
148
 
        })
149
 
}
150
 
 
151
 
func (s *ToolsSuite) TestTools(c *gc.C) {
152
 
        _, _, err := s.storage.Tools(current)
153
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
154
 
        c.Assert(err, gc.ErrorMatches, `.* tools metadata not found`)
155
 
 
156
 
        s.addMetadataDoc(c, current, 3, "hash(abc)", "path")
157
 
        _, _, err = s.storage.Tools(current)
158
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
159
 
        c.Assert(err, gc.ErrorMatches, `resource at path "buckets/my-uuid/path" not found`)
160
 
 
161
 
        err = s.managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
162
 
        c.Assert(err, jc.ErrorIsNil)
163
 
 
164
 
        metadata, r, err := s.storage.Tools(current)
165
 
        c.Assert(err, jc.ErrorIsNil)
166
 
        defer r.Close()
167
 
        c.Assert(metadata, gc.Equals, toolstorage.Metadata{
168
 
                Version: current,
169
 
                Size:    3,
170
 
                SHA256:  "hash(abc)",
171
 
        })
172
 
 
173
 
        data, err := ioutil.ReadAll(r)
174
 
        c.Assert(err, jc.ErrorIsNil)
175
 
        c.Assert(string(data), gc.Equals, "blah")
176
 
}
177
 
 
178
 
func (s *ToolsSuite) TestAddToolsRemovesExisting(c *gc.C) {
179
 
        // Add a metadata doc and a blob at a known path, then
180
 
        // call AddTools and ensure the original blob is removed.
181
 
        s.addMetadataDoc(c, current, 3, "hash(abc)", "path")
182
 
        err := s.managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
183
 
        c.Assert(err, jc.ErrorIsNil)
184
 
 
185
 
        addedMetadata := toolstorage.Metadata{
186
 
                Version: current,
187
 
                Size:    6,
188
 
                SHA256:  "hash(xyzzzz)",
189
 
        }
190
 
        err = s.storage.AddTools(strings.NewReader("xyzzzz"), addedMetadata)
191
 
        c.Assert(err, jc.ErrorIsNil)
192
 
 
193
 
        // old blob should be gone
194
 
        _, _, err = s.managedStorage.GetForBucket("my-uuid", "path")
195
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
196
 
 
197
 
        s.assertTools(c, addedMetadata, "xyzzzz")
198
 
}
199
 
 
200
 
func (s *ToolsSuite) TestAddToolsRemovesExistingRemoveFails(c *gc.C) {
201
 
        // Add a metadata doc and a blob at a known path, then
202
 
        // call AddTools and ensure that AddTools attempts to remove
203
 
        // the original blob, but does not return an error if it
204
 
        // fails.
205
 
        s.addMetadataDoc(c, current, 3, "hash(abc)", "path")
206
 
        err := s.managedStorage.PutForBucket("my-uuid", "path", strings.NewReader("blah"), 4)
207
 
        c.Assert(err, jc.ErrorIsNil)
208
 
 
209
 
        storage := toolstorage.NewStorage(
210
 
                "my-uuid",
211
 
                removeFailsManagedStorage{s.managedStorage},
212
 
                s.metadataCollection,
213
 
                s.txnRunner,
214
 
        )
215
 
        addedMetadata := toolstorage.Metadata{
216
 
                Version: current,
217
 
                Size:    6,
218
 
                SHA256:  "hash(xyzzzz)",
219
 
        }
220
 
        err = storage.AddTools(strings.NewReader("xyzzzz"), addedMetadata)
221
 
        c.Assert(err, jc.ErrorIsNil)
222
 
 
223
 
        // old blob should still be there
224
 
        r, _, err := s.managedStorage.GetForBucket("my-uuid", "path")
225
 
        c.Assert(err, jc.ErrorIsNil)
226
 
        r.Close()
227
 
 
228
 
        s.assertTools(c, addedMetadata, "xyzzzz")
229
 
}
230
 
 
231
 
func (s *ToolsSuite) TestAddToolsRemovesBlobOnFailure(c *gc.C) {
232
 
        storage := toolstorage.NewStorage(
233
 
                "my-uuid",
234
 
                s.managedStorage,
235
 
                s.metadataCollection,
236
 
                errorTransactionRunner{s.txnRunner},
237
 
        )
238
 
        addedMetadata := toolstorage.Metadata{
239
 
                Version: current,
240
 
                Size:    6,
241
 
                SHA256:  "hash",
242
 
        }
243
 
        err := storage.AddTools(strings.NewReader("xyzzzz"), addedMetadata)
244
 
        c.Assert(err, gc.ErrorMatches, "cannot store tools metadata: Run fails")
245
 
 
246
 
        path := fmt.Sprintf("tools/%s-%s", addedMetadata.Version, addedMetadata.SHA256)
247
 
        _, _, err = s.managedStorage.GetForBucket("my-uuid", path)
248
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
249
 
}
250
 
 
251
 
func (s *ToolsSuite) TestAddToolsRemovesBlobOnFailureRemoveFails(c *gc.C) {
252
 
        storage := toolstorage.NewStorage(
253
 
                "my-uuid",
254
 
                removeFailsManagedStorage{s.managedStorage},
255
 
                s.metadataCollection,
256
 
                errorTransactionRunner{s.txnRunner},
257
 
        )
258
 
        addedMetadata := toolstorage.Metadata{
259
 
                Version: current,
260
 
                Size:    6,
261
 
                SHA256:  "hash",
262
 
        }
263
 
        err := storage.AddTools(strings.NewReader("xyzzzz"), addedMetadata)
264
 
        c.Assert(err, gc.ErrorMatches, "cannot store tools metadata: Run fails")
265
 
 
266
 
        // blob should still be there, because the removal failed.
267
 
        path := fmt.Sprintf("tools/%s-%s", addedMetadata.Version, addedMetadata.SHA256)
268
 
        r, _, err := s.managedStorage.GetForBucket("my-uuid", path)
269
 
        c.Assert(err, jc.ErrorIsNil)
270
 
        r.Close()
271
 
}
272
 
 
273
 
func (s *ToolsSuite) TestAddToolsSame(c *gc.C) {
274
 
        metadata := toolstorage.Metadata{Version: current, Size: 1, SHA256: "0"}
275
 
        for i := 0; i < 2; i++ {
276
 
                err := s.storage.AddTools(strings.NewReader("0"), metadata)
277
 
                c.Assert(err, jc.ErrorIsNil)
278
 
                s.assertTools(c, metadata, "0")
279
 
        }
280
 
}
281
 
 
282
 
func (s *ToolsSuite) TestAddToolsConcurrent(c *gc.C) {
283
 
        metadata0 := toolstorage.Metadata{Version: current, Size: 1, SHA256: "0"}
284
 
        metadata1 := toolstorage.Metadata{Version: current, Size: 1, SHA256: "1"}
285
 
 
286
 
        addMetadata := func() {
287
 
                err := s.storage.AddTools(strings.NewReader("0"), metadata0)
288
 
                c.Assert(err, jc.ErrorIsNil)
289
 
                r, _, err := s.managedStorage.GetForBucket("my-uuid", fmt.Sprintf("tools/%s-0", current))
290
 
                c.Assert(err, jc.ErrorIsNil)
291
 
                r.Close()
292
 
        }
293
 
        defer txntesting.SetBeforeHooks(c, s.txnRunner, addMetadata).Check()
294
 
 
295
 
        err := s.storage.AddTools(strings.NewReader("1"), metadata1)
296
 
        c.Assert(err, jc.ErrorIsNil)
297
 
 
298
 
        // Blob added in before-hook should be removed.
299
 
        _, _, err = s.managedStorage.GetForBucket("my-uuid", fmt.Sprintf("tools/%s-0", current))
300
 
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
301
 
 
302
 
        s.assertTools(c, metadata1, "1")
303
 
}
304
 
 
305
 
func (s *ToolsSuite) TestAddToolsExcessiveContention(c *gc.C) {
306
 
        metadata := []toolstorage.Metadata{
307
 
                {Version: current, Size: 1, SHA256: "0"},
308
 
                {Version: current, Size: 1, SHA256: "1"},
309
 
                {Version: current, Size: 1, SHA256: "2"},
310
 
                {Version: current, Size: 1, SHA256: "3"},
311
 
        }
312
 
 
313
 
        i := 1
314
 
        addMetadata := func() {
315
 
                err := s.storage.AddTools(strings.NewReader(metadata[i].SHA256), metadata[i])
316
 
                c.Assert(err, jc.ErrorIsNil)
317
 
                i++
318
 
        }
319
 
        defer txntesting.SetBeforeHooks(c, s.txnRunner, addMetadata, addMetadata, addMetadata).Check()
320
 
 
321
 
        err := s.storage.AddTools(strings.NewReader(metadata[0].SHA256), metadata[0])
322
 
        c.Assert(err, gc.ErrorMatches, "cannot store tools metadata: state changing too quickly; try again soon")
323
 
 
324
 
        // There should be no blobs apart from the last one added by the before-hook.
325
 
        for _, metadata := range metadata[:3] {
326
 
                path := fmt.Sprintf("tools/%s-%s", metadata.Version, metadata.SHA256)
327
 
                _, _, err = s.managedStorage.GetForBucket("my-uuid", path)
328
 
                c.Assert(err, jc.Satisfies, errors.IsNotFound)
329
 
        }
330
 
 
331
 
        s.assertTools(c, metadata[3], "3")
332
 
}
333
 
 
334
 
func (s *ToolsSuite) addMetadataDoc(c *gc.C, v version.Binary, size int64, hash, path string) {
335
 
        doc := struct {
336
 
                Id      string         `bson:"_id"`
337
 
                Version version.Binary `bson:"version"`
338
 
                Size    int64          `bson:"size"`
339
 
                SHA256  string         `bson:"sha256,omitempty"`
340
 
                Path    string         `bson:"path"`
341
 
        }{
342
 
                Id:      v.String(),
343
 
                Version: v,
344
 
                Size:    size,
345
 
                SHA256:  hash,
346
 
                Path:    path,
347
 
        }
348
 
        err := s.metadataCollection.Insert(&doc)
349
 
        c.Assert(err, jc.ErrorIsNil)
350
 
}
351
 
 
352
 
func (s *ToolsSuite) assertTools(c *gc.C, expected toolstorage.Metadata, content string) {
353
 
        metadata, r, err := s.storage.Tools(expected.Version)
354
 
        c.Assert(err, jc.ErrorIsNil)
355
 
        defer r.Close()
356
 
        c.Assert(metadata, gc.Equals, expected)
357
 
 
358
 
        data, err := ioutil.ReadAll(r)
359
 
        c.Assert(err, jc.ErrorIsNil)
360
 
        c.Assert(string(data), gc.Equals, content)
361
 
}
362
 
 
363
 
type removeFailsManagedStorage struct {
364
 
        blobstore.ManagedStorage
365
 
}
366
 
 
367
 
func (removeFailsManagedStorage) RemoveForBucket(uuid, path string) error {
368
 
        return errors.Errorf("cannot remove %s:%s", uuid, path)
369
 
}
370
 
 
371
 
type errorTransactionRunner struct {
372
 
        jujutxn.Runner
373
 
}
374
 
 
375
 
func (errorTransactionRunner) Run(transactions jujutxn.TransactionSource) error {
376
 
        return errors.New("Run fails")
377
 
}