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

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/migration/migration_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 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package migration_test
 
5
 
 
6
import (
 
7
        "bytes"
 
8
        "fmt"
 
9
        "io"
 
10
        "io/ioutil"
 
11
 
 
12
        "github.com/juju/errors"
 
13
        "github.com/juju/names"
 
14
        jc "github.com/juju/testing/checkers"
 
15
        "github.com/juju/utils"
 
16
        "github.com/juju/version"
 
17
        gc "gopkg.in/check.v1"
 
18
        "gopkg.in/juju/charm.v6-unstable"
 
19
        "gopkg.in/mgo.v2"
 
20
 
 
21
        "github.com/juju/juju/api"
 
22
        "github.com/juju/juju/cmd/modelcmd"
 
23
        "github.com/juju/juju/core/description"
 
24
        "github.com/juju/juju/environs"
 
25
        "github.com/juju/juju/jujuclient/jujuclienttesting"
 
26
        "github.com/juju/juju/migration"
 
27
        "github.com/juju/juju/provider/dummy"
 
28
        _ "github.com/juju/juju/provider/dummy"
 
29
        "github.com/juju/juju/state"
 
30
        "github.com/juju/juju/state/binarystorage"
 
31
        "github.com/juju/juju/state/storage"
 
32
        statetesting "github.com/juju/juju/state/testing"
 
33
        "github.com/juju/juju/testing"
 
34
        "github.com/juju/juju/tools"
 
35
)
 
36
 
 
37
type ImportSuite struct {
 
38
        statetesting.StateSuite
 
39
}
 
40
 
 
41
var _ = gc.Suite(&ImportSuite{})
 
42
 
 
43
func (s *ImportSuite) SetUpTest(c *gc.C) {
 
44
        // Specify the config to use for the controller model before calling
 
45
        // SetUpTest of the StateSuite, otherwise we get testing.ModelConfig(c).
 
46
        // The default provider type specified in the testing.ModelConfig function
 
47
        // is one that isn't registered as a valid provider. For our tests here we
 
48
        // need a real registered provider, so we use the dummy provider.
 
49
        // NOTE: make a better test provider.
 
50
        env, err := environs.Prepare(
 
51
                modelcmd.BootstrapContext(testing.Context(c)),
 
52
                jujuclienttesting.NewMemStore(),
 
53
                environs.PrepareParams{
 
54
                        ControllerName: "dummycontroller",
 
55
                        BaseConfig:     dummy.SampleConfig(),
 
56
                        CloudName:      "dummy",
 
57
                },
 
58
        )
 
59
        c.Assert(err, jc.ErrorIsNil)
 
60
 
 
61
        s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())
 
62
        s.StateSuite.SetUpTest(c)
 
63
}
 
64
 
 
65
func (s *ImportSuite) TestBadBytes(c *gc.C) {
 
66
        bytes := []byte("not a model")
 
67
        model, st, err := migration.ImportModel(s.State, bytes)
 
68
        c.Check(st, gc.IsNil)
 
69
        c.Check(model, gc.IsNil)
 
70
        c.Assert(err, gc.ErrorMatches, "yaml: unmarshal errors:\n.*")
 
71
}
 
72
 
 
73
func (s *ImportSuite) TestImportModel(c *gc.C) {
 
74
        model, err := s.State.Export()
 
75
        c.Check(err, jc.ErrorIsNil)
 
76
 
 
77
        controllerConfig, err := s.State.ModelConfig()
 
78
        c.Check(err, jc.ErrorIsNil)
 
79
 
 
80
        // Update the config values in the exported model for different values for
 
81
        // "state-port", "api-port", and "ca-cert". Also give the model a new UUID
 
82
        // and name so we can import it nicely.
 
83
        model.UpdateConfig(map[string]interface{}{
 
84
                "name":       "new-model",
 
85
                "uuid":       utils.MustNewUUID().String(),
 
86
                "state-port": 12345,
 
87
                "api-port":   54321,
 
88
                "ca-cert":    "not really a cert",
 
89
        })
 
90
 
 
91
        bytes, err := description.Serialize(model)
 
92
        c.Check(err, jc.ErrorIsNil)
 
93
 
 
94
        dbModel, dbState, err := migration.ImportModel(s.State, bytes)
 
95
        c.Check(err, jc.ErrorIsNil)
 
96
        defer dbState.Close()
 
97
 
 
98
        dbConfig, err := dbModel.Config()
 
99
        c.Assert(err, jc.ErrorIsNil)
 
100
        attrs := dbConfig.AllAttrs()
 
101
        c.Assert(attrs["state-port"], gc.Equals, controllerConfig.StatePort())
 
102
        c.Assert(attrs["api-port"], gc.Equals, controllerConfig.APIPort())
 
103
        cacert, ok := controllerConfig.CACert()
 
104
        c.Assert(ok, jc.IsTrue)
 
105
        c.Assert(attrs["ca-cert"], gc.Equals, cacert)
 
106
        c.Assert(attrs["controller-uuid"], gc.Equals, controllerConfig.UUID())
 
107
}
 
108
 
 
109
func (s *ImportSuite) TestUploadBinariesTools(c *gc.C) {
 
110
        // Create a model that has three different tools versions:
 
111
        // one for a machine, one for a container, and one for a unit agent.
 
112
        // We don't care about the actual validity of the model (it isn't).
 
113
        model := description.NewModel(description.ModelArgs{
 
114
                Owner: names.NewUserTag("me"),
 
115
        })
 
116
        machine := model.AddMachine(description.MachineArgs{
 
117
                Id: names.NewMachineTag("0"),
 
118
        })
 
119
        machine.SetTools(description.AgentToolsArgs{
 
120
                Version: version.MustParseBinary("2.0.1-trusty-amd64"),
 
121
        })
 
122
        container := machine.AddContainer(description.MachineArgs{
 
123
                Id: names.NewMachineTag("0/lxc/0"),
 
124
        })
 
125
        container.SetTools(description.AgentToolsArgs{
 
126
                Version: version.MustParseBinary("2.0.5-trusty-amd64"),
 
127
        })
 
128
        service := model.AddService(description.ServiceArgs{
 
129
                Tag:      names.NewServiceTag("magic"),
 
130
                CharmURL: "local:trusty/magic",
 
131
        })
 
132
        unit := service.AddUnit(description.UnitArgs{
 
133
                Tag: names.NewUnitTag("magic/0"),
 
134
        })
 
135
        unit.SetTools(description.AgentToolsArgs{
 
136
                Version: version.MustParseBinary("2.0.3-trusty-amd64"),
 
137
        })
 
138
 
 
139
        uploader := &fakeUploader{tools: make(map[version.Binary]string)}
 
140
        config := migration.UploadBinariesConfig{
 
141
                State:            &fakeStateStorage{},
 
142
                Model:            model,
 
143
                Target:           &fakeAPIConnection{},
 
144
                GetCharmUploader: func(api.Connection) migration.CharmUploader { return &noOpUploader{} },
 
145
                GetToolsUploader: func(target api.Connection) migration.ToolsUploader {
 
146
                        return uploader
 
147
                },
 
148
                GetStateStorage:     func(migration.UploadBackend) storage.Storage { return &fakeCharmsStorage{} },
 
149
                GetCharmStoragePath: func(migration.UploadBackend, *charm.URL) (string, error) { return "", nil },
 
150
        }
 
151
        err := migration.UploadBinaries(config)
 
152
        c.Assert(err, jc.ErrorIsNil)
 
153
 
 
154
        c.Assert(uploader.tools, jc.DeepEquals, map[version.Binary]string{
 
155
                version.MustParseBinary("2.0.1-trusty-amd64"): "fake tools 2.0.1-trusty-amd64",
 
156
                version.MustParseBinary("2.0.3-trusty-amd64"): "fake tools 2.0.3-trusty-amd64",
 
157
                version.MustParseBinary("2.0.5-trusty-amd64"): "fake tools 2.0.5-trusty-amd64",
 
158
        })
 
159
}
 
160
 
 
161
func (s *ImportSuite) TestStreamCharmsTools(c *gc.C) {
 
162
        model := description.NewModel(description.ModelArgs{
 
163
                Owner: names.NewUserTag("me"),
 
164
        })
 
165
        model.AddService(description.ServiceArgs{
 
166
                Tag:      names.NewServiceTag("magic"),
 
167
                CharmURL: "local:trusty/magic",
 
168
        })
 
169
        model.AddService(description.ServiceArgs{
 
170
                Tag:      names.NewServiceTag("magic"),
 
171
                CharmURL: "cs:trusty/postgresql-42",
 
172
        })
 
173
 
 
174
        uploader := &fakeUploader{charms: make(map[string]string)}
 
175
        config := migration.UploadBinariesConfig{
 
176
                State:            &fakeStateStorage{},
 
177
                Model:            model,
 
178
                Target:           &fakeAPIConnection{},
 
179
                GetCharmUploader: func(api.Connection) migration.CharmUploader { return uploader },
 
180
                GetToolsUploader: func(target api.Connection) migration.ToolsUploader { return &noOpUploader{} },
 
181
                GetStateStorage:  func(migration.UploadBackend) storage.Storage { return &fakeCharmsStorage{} },
 
182
                GetCharmStoragePath: func(_ migration.UploadBackend, u *charm.URL) (string, error) {
 
183
                        return "/path/for/" + u.String(), nil
 
184
                },
 
185
        }
 
186
        err := migration.UploadBinaries(config)
 
187
        c.Assert(err, jc.ErrorIsNil)
 
188
 
 
189
        c.Assert(uploader.charms, jc.DeepEquals, map[string]string{
 
190
                "local:trusty/magic":      "fake file at /path/for/local:trusty/magic",
 
191
                "cs:trusty/postgresql-42": "fake file at /path/for/cs:trusty/postgresql-42",
 
192
        })
 
193
}
 
194
 
 
195
type fakeStateStorage struct {
 
196
        tools  fakeToolsStorage
 
197
        charms fakeCharmsStorage
 
198
}
 
199
 
 
200
type fakeCharmsStorage struct {
 
201
        storage.Storage
 
202
}
 
203
 
 
204
type fakeAPIConnection struct {
 
205
        api.Connection
 
206
}
 
207
 
 
208
type fakeToolsStorage struct {
 
209
        binarystorage.Storage
 
210
        closed bool
 
211
}
 
212
 
 
213
func (f *fakeStateStorage) ToolsStorage() (binarystorage.StorageCloser, error) {
 
214
        return &f.tools, nil
 
215
}
 
216
 
 
217
func (f *fakeStateStorage) ModelUUID() string {
 
218
        return testing.ModelTag.Id()
 
219
}
 
220
 
 
221
func (f *fakeStateStorage) MongoSession() *mgo.Session {
 
222
        return nil
 
223
}
 
224
 
 
225
func (f *fakeStateStorage) Charm(*charm.URL) (*state.Charm, error) {
 
226
        return nil, nil
 
227
}
 
228
 
 
229
func (f *fakeToolsStorage) Open(v string) (binarystorage.Metadata, io.ReadCloser, error) {
 
230
        buff := bytes.NewBufferString(fmt.Sprintf("fake tools %s", v))
 
231
        return binarystorage.Metadata{}, ioutil.NopCloser(buff), nil
 
232
}
 
233
 
 
234
func (f *fakeToolsStorage) Close() error {
 
235
        f.closed = true
 
236
        return nil
 
237
}
 
238
 
 
239
func (f *fakeCharmsStorage) Get(path string) (io.ReadCloser, int64, error) {
 
240
        buff := bytes.NewBufferString(fmt.Sprintf("fake file at %s", path))
 
241
        return ioutil.NopCloser(buff), int64(buff.Len()), nil
 
242
}
 
243
 
 
244
type fakeUploader struct {
 
245
        tools  map[version.Binary]string
 
246
        charms map[string]string
 
247
}
 
248
 
 
249
func (f *fakeUploader) UploadTools(r io.ReadSeeker, v version.Binary, _ ...string) (*tools.Tools, error) {
 
250
        data, err := ioutil.ReadAll(r)
 
251
        if err != nil {
 
252
                return nil, errors.Trace(err)
 
253
        }
 
254
 
 
255
        f.tools[v] = string(data)
 
256
 
 
257
        return &tools.Tools{
 
258
                Version: v,
 
259
        }, nil
 
260
}
 
261
 
 
262
func (f *fakeUploader) UploadCharm(u *charm.URL, r io.ReadSeeker) (*charm.URL, error) {
 
263
        data, err := ioutil.ReadAll(r)
 
264
        if err != nil {
 
265
                return nil, errors.Trace(err)
 
266
        }
 
267
 
 
268
        f.charms[u.String()] = string(data)
 
269
        return u, nil
 
270
}
 
271
 
 
272
type noOpUploader struct{}
 
273
 
 
274
func (*noOpUploader) UploadCharm(*charm.URL, io.ReadSeeker) (*charm.URL, error) {
 
275
        return nil, nil
 
276
}
 
277
 
 
278
func (*noOpUploader) UploadTools(io.ReadSeeker, version.Binary, ...string) (*tools.Tools, error) {
 
279
        return nil, nil
 
280
}
 
281
 
 
282
type ExportSuite struct {
 
283
        statetesting.StateSuite
 
284
}
 
285
 
 
286
var _ = gc.Suite(&ExportSuite{})
 
287
 
 
288
func (s *ExportSuite) TestExportModel(c *gc.C) {
 
289
        bytes, err := migration.ExportModel(s.State)
 
290
        c.Assert(err, jc.ErrorIsNil)
 
291
        // The bytes must be a valid model.
 
292
        _, err = description.Deserialize(bytes)
 
293
        c.Assert(err, jc.ErrorIsNil)
 
294
}
 
295
 
 
296
type PrecheckSuite struct {
 
297
        testing.BaseSuite
 
298
}
 
299
 
 
300
var _ = gc.Suite(&PrecheckSuite{})
 
301
 
 
302
// Assert that *state.State implements the PrecheckBackend
 
303
var _ migration.PrecheckBackend = (*state.State)(nil)
 
304
 
 
305
func (*PrecheckSuite) TestPrecheckCleanups(c *gc.C) {
 
306
        backend := &fakePrecheckBackend{}
 
307
        err := migration.Precheck(backend)
 
308
        c.Assert(err, jc.ErrorIsNil)
 
309
}
 
310
 
 
311
func (*PrecheckSuite) TestPrecheckCleanupsError(c *gc.C) {
 
312
        backend := &fakePrecheckBackend{
 
313
                cleanupError: errors.New("boom"),
 
314
        }
 
315
        err := migration.Precheck(backend)
 
316
        c.Assert(err, gc.ErrorMatches, "precheck cleanups: boom")
 
317
}
 
318
 
 
319
func (*PrecheckSuite) TestPrecheckCleanupsNeeded(c *gc.C) {
 
320
        backend := &fakePrecheckBackend{
 
321
                cleanupNeeded: true,
 
322
        }
 
323
        err := migration.Precheck(backend)
 
324
        c.Assert(err, gc.ErrorMatches, "precheck failed: cleanup needed")
 
325
}
 
326
 
 
327
type fakePrecheckBackend struct {
 
328
        cleanupNeeded bool
 
329
        cleanupError  error
 
330
}
 
331
 
 
332
func (f *fakePrecheckBackend) NeedsCleanup() (bool, error) {
 
333
        return f.cleanupNeeded, f.cleanupError
 
334
}
 
335
 
 
336
type InternalSuite struct {
 
337
        testing.BaseSuite
 
338
}
 
339
 
 
340
var _ = gc.Suite(&InternalSuite{})
 
341
 
 
342
func (s *InternalSuite) TestControllerValues(c *gc.C) {
 
343
        config := testing.ModelConfig(c)
 
344
        fields := migration.ControllerValues(config)
 
345
        c.Assert(fields, jc.DeepEquals, map[string]interface{}{
 
346
                "controller-uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d",
 
347
                "state-port":      19034,
 
348
                "api-port":        17777,
 
349
                "ca-cert":         testing.CACert,
 
350
        })
 
351
}
 
352
 
 
353
func (s *InternalSuite) TestUpdateConfigFromProvider(c *gc.C) {
 
354
        controllerConfig := testing.ModelConfig(c)
 
355
        configAttrs := testing.FakeConfig()
 
356
        configAttrs["type"] = "dummy"
 
357
        // Fake the "state-id" so the provider thinks it is prepared already.
 
358
        configAttrs["state-id"] = "42"
 
359
        // We need to specify a valid provider type, so we use dummy.
 
360
        // The dummy provider grabs the UUID from the controller config
 
361
        // and returns it in the map with the key "controller-uuid", similar
 
362
        // to what the azure provider will need to do.
 
363
        model := description.NewModel(description.ModelArgs{
 
364
                Owner:  names.NewUserTag("test-admin"),
 
365
                Config: configAttrs,
 
366
        })
 
367
 
 
368
        err := migration.UpdateConfigFromProvider(model, controllerConfig)
 
369
        c.Assert(err, jc.ErrorIsNil)
 
370
 
 
371
        modelConfig := model.Config()
 
372
        c.Assert(modelConfig["controller-uuid"], gc.Equals, controllerConfig.UUID())
 
373
}
 
374
 
 
375
type CharmInternalSuite struct {
 
376
        statetesting.StateSuite
 
377
}
 
378
 
 
379
var _ = gc.Suite(&CharmInternalSuite{})
 
380
 
 
381
func (s *CharmInternalSuite) TestCharmStoragePath(c *gc.C) {
 
382
        charm := s.Factory.MakeCharm(c, nil)
 
383
 
 
384
        path, err := migration.GetCharmStoragePath(s.State, charm.URL())
 
385
        c.Assert(err, jc.ErrorIsNil)
 
386
        c.Assert(path, gc.Equals, "fake-storage-path")
 
387
}