1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
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"
37
type ImportSuite struct {
38
statetesting.StateSuite
41
var _ = gc.Suite(&ImportSuite{})
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(),
59
c.Assert(err, jc.ErrorIsNil)
61
s.InitialConfig = testing.CustomModelConfig(c, env.Config().AllAttrs())
62
s.StateSuite.SetUpTest(c)
65
func (s *ImportSuite) TestBadBytes(c *gc.C) {
66
bytes := []byte("not a model")
67
model, st, err := migration.ImportModel(s.State, bytes)
69
c.Check(model, gc.IsNil)
70
c.Assert(err, gc.ErrorMatches, "yaml: unmarshal errors:\n.*")
73
func (s *ImportSuite) TestImportModel(c *gc.C) {
74
model, err := s.State.Export()
75
c.Check(err, jc.ErrorIsNil)
77
controllerConfig, err := s.State.ModelConfig()
78
c.Check(err, jc.ErrorIsNil)
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{}{
85
"uuid": utils.MustNewUUID().String(),
88
"ca-cert": "not really a cert",
91
bytes, err := description.Serialize(model)
92
c.Check(err, jc.ErrorIsNil)
94
dbModel, dbState, err := migration.ImportModel(s.State, bytes)
95
c.Check(err, jc.ErrorIsNil)
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())
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"),
116
machine := model.AddMachine(description.MachineArgs{
117
Id: names.NewMachineTag("0"),
119
machine.SetTools(description.AgentToolsArgs{
120
Version: version.MustParseBinary("2.0.1-trusty-amd64"),
122
container := machine.AddContainer(description.MachineArgs{
123
Id: names.NewMachineTag("0/lxc/0"),
125
container.SetTools(description.AgentToolsArgs{
126
Version: version.MustParseBinary("2.0.5-trusty-amd64"),
128
service := model.AddService(description.ServiceArgs{
129
Tag: names.NewServiceTag("magic"),
130
CharmURL: "local:trusty/magic",
132
unit := service.AddUnit(description.UnitArgs{
133
Tag: names.NewUnitTag("magic/0"),
135
unit.SetTools(description.AgentToolsArgs{
136
Version: version.MustParseBinary("2.0.3-trusty-amd64"),
139
uploader := &fakeUploader{tools: make(map[version.Binary]string)}
140
config := migration.UploadBinariesConfig{
141
State: &fakeStateStorage{},
143
Target: &fakeAPIConnection{},
144
GetCharmUploader: func(api.Connection) migration.CharmUploader { return &noOpUploader{} },
145
GetToolsUploader: func(target api.Connection) migration.ToolsUploader {
148
GetStateStorage: func(migration.UploadBackend) storage.Storage { return &fakeCharmsStorage{} },
149
GetCharmStoragePath: func(migration.UploadBackend, *charm.URL) (string, error) { return "", nil },
151
err := migration.UploadBinaries(config)
152
c.Assert(err, jc.ErrorIsNil)
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",
161
func (s *ImportSuite) TestStreamCharmsTools(c *gc.C) {
162
model := description.NewModel(description.ModelArgs{
163
Owner: names.NewUserTag("me"),
165
model.AddService(description.ServiceArgs{
166
Tag: names.NewServiceTag("magic"),
167
CharmURL: "local:trusty/magic",
169
model.AddService(description.ServiceArgs{
170
Tag: names.NewServiceTag("magic"),
171
CharmURL: "cs:trusty/postgresql-42",
174
uploader := &fakeUploader{charms: make(map[string]string)}
175
config := migration.UploadBinariesConfig{
176
State: &fakeStateStorage{},
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
186
err := migration.UploadBinaries(config)
187
c.Assert(err, jc.ErrorIsNil)
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",
195
type fakeStateStorage struct {
196
tools fakeToolsStorage
197
charms fakeCharmsStorage
200
type fakeCharmsStorage struct {
204
type fakeAPIConnection struct {
208
type fakeToolsStorage struct {
209
binarystorage.Storage
213
func (f *fakeStateStorage) ToolsStorage() (binarystorage.StorageCloser, error) {
217
func (f *fakeStateStorage) ModelUUID() string {
218
return testing.ModelTag.Id()
221
func (f *fakeStateStorage) MongoSession() *mgo.Session {
225
func (f *fakeStateStorage) Charm(*charm.URL) (*state.Charm, error) {
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
234
func (f *fakeToolsStorage) Close() error {
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
244
type fakeUploader struct {
245
tools map[version.Binary]string
246
charms map[string]string
249
func (f *fakeUploader) UploadTools(r io.ReadSeeker, v version.Binary, _ ...string) (*tools.Tools, error) {
250
data, err := ioutil.ReadAll(r)
252
return nil, errors.Trace(err)
255
f.tools[v] = string(data)
262
func (f *fakeUploader) UploadCharm(u *charm.URL, r io.ReadSeeker) (*charm.URL, error) {
263
data, err := ioutil.ReadAll(r)
265
return nil, errors.Trace(err)
268
f.charms[u.String()] = string(data)
272
type noOpUploader struct{}
274
func (*noOpUploader) UploadCharm(*charm.URL, io.ReadSeeker) (*charm.URL, error) {
278
func (*noOpUploader) UploadTools(io.ReadSeeker, version.Binary, ...string) (*tools.Tools, error) {
282
type ExportSuite struct {
283
statetesting.StateSuite
286
var _ = gc.Suite(&ExportSuite{})
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)
296
type PrecheckSuite struct {
300
var _ = gc.Suite(&PrecheckSuite{})
302
// Assert that *state.State implements the PrecheckBackend
303
var _ migration.PrecheckBackend = (*state.State)(nil)
305
func (*PrecheckSuite) TestPrecheckCleanups(c *gc.C) {
306
backend := &fakePrecheckBackend{}
307
err := migration.Precheck(backend)
308
c.Assert(err, jc.ErrorIsNil)
311
func (*PrecheckSuite) TestPrecheckCleanupsError(c *gc.C) {
312
backend := &fakePrecheckBackend{
313
cleanupError: errors.New("boom"),
315
err := migration.Precheck(backend)
316
c.Assert(err, gc.ErrorMatches, "precheck cleanups: boom")
319
func (*PrecheckSuite) TestPrecheckCleanupsNeeded(c *gc.C) {
320
backend := &fakePrecheckBackend{
323
err := migration.Precheck(backend)
324
c.Assert(err, gc.ErrorMatches, "precheck failed: cleanup needed")
327
type fakePrecheckBackend struct {
332
func (f *fakePrecheckBackend) NeedsCleanup() (bool, error) {
333
return f.cleanupNeeded, f.cleanupError
336
type InternalSuite struct {
340
var _ = gc.Suite(&InternalSuite{})
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",
349
"ca-cert": testing.CACert,
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"),
368
err := migration.UpdateConfigFromProvider(model, controllerConfig)
369
c.Assert(err, jc.ErrorIsNil)
371
modelConfig := model.Config()
372
c.Assert(modelConfig["controller-uuid"], gc.Equals, controllerConfig.UUID())
375
type CharmInternalSuite struct {
376
statetesting.StateSuite
379
var _ = gc.Suite(&CharmInternalSuite{})
381
func (s *CharmInternalSuite) TestCharmStoragePath(c *gc.C) {
382
charm := s.Factory.MakeCharm(c, nil)
384
path, err := migration.GetCharmStoragePath(s.State, charm.URL())
385
c.Assert(err, jc.ErrorIsNil)
386
c.Assert(path, gc.Equals, "fake-storage-path")