~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/controller/addmodel_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 2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package controller_test
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "io/ioutil"
 
9
 
 
10
        "github.com/juju/cmd"
 
11
        "github.com/juju/errors"
 
12
        jc "github.com/juju/testing/checkers"
 
13
        "github.com/juju/utils"
 
14
        gc "gopkg.in/check.v1"
 
15
        "gopkg.in/yaml.v2"
 
16
 
 
17
        "github.com/juju/juju/api"
 
18
        "github.com/juju/juju/apiserver/params"
 
19
        "github.com/juju/juju/cloud"
 
20
        "github.com/juju/juju/cmd/juju/controller"
 
21
        "github.com/juju/juju/jujuclient"
 
22
        "github.com/juju/juju/jujuclient/jujuclienttesting"
 
23
        _ "github.com/juju/juju/provider/ec2"
 
24
        "github.com/juju/juju/testing"
 
25
        "gopkg.in/juju/names.v2"
 
26
)
 
27
 
 
28
type addSuite struct {
 
29
        testing.FakeJujuXDGDataHomeSuite
 
30
        fakeAddModelAPI *fakeAddClient
 
31
        fakeCloundAPI   *fakeCloudAPI
 
32
        store           *jujuclienttesting.MemStore
 
33
}
 
34
 
 
35
var _ = gc.Suite(&addSuite{})
 
36
 
 
37
func (s *addSuite) SetUpTest(c *gc.C) {
 
38
        s.FakeJujuXDGDataHomeSuite.SetUpTest(c)
 
39
        s.fakeAddModelAPI = &fakeAddClient{
 
40
                model: params.ModelInfo{
 
41
                        Name:     "test",
 
42
                        UUID:     "fake-model-uuid",
 
43
                        OwnerTag: "ignored-for-now",
 
44
                },
 
45
        }
 
46
        s.fakeCloundAPI = &fakeCloudAPI{}
 
47
 
 
48
        // Set up the current controller, and write just enough info
 
49
        // so we don't try to refresh
 
50
        controllerName := "test-master"
 
51
        s.store = jujuclienttesting.NewMemStore()
 
52
        s.store.CurrentControllerName = controllerName
 
53
        s.store.Controllers[controllerName] = jujuclient.ControllerDetails{}
 
54
        s.store.Accounts[controllerName] = jujuclient.AccountDetails{
 
55
                User: "bob@local",
 
56
        }
 
57
        s.store.Credentials["aws"] = cloud.CloudCredential{
 
58
                AuthCredentials: map[string]cloud.Credential{
 
59
                        "secrets": cloud.NewCredential(cloud.AccessKeyAuthType, map[string]string{
 
60
                                "access-key": "key",
 
61
                                "secret-key": "sekret",
 
62
                        }),
 
63
                },
 
64
        }
 
65
}
 
66
 
 
67
type fakeAPIConnection struct {
 
68
        api.Connection
 
69
}
 
70
 
 
71
func (*fakeAPIConnection) Close() error {
 
72
        return nil
 
73
}
 
74
 
 
75
func (s *addSuite) run(c *gc.C, args ...string) (*cmd.Context, error) {
 
76
        command, _ := controller.NewAddModelCommandForTest(&fakeAPIConnection{}, s.fakeAddModelAPI, s.fakeCloundAPI, s.store)
 
77
        return testing.RunCommand(c, command, args...)
 
78
}
 
79
 
 
80
func (s *addSuite) TestInit(c *gc.C) {
 
81
        modelNameErr := "%q is not a valid name: model names may only contain lowercase letters, digits and hyphens"
 
82
        for i, test := range []struct {
 
83
                args   []string
 
84
                err    string
 
85
                name   string
 
86
                owner  string
 
87
                values map[string]interface{}
 
88
        }{
 
89
                {
 
90
                        err: "model name is required",
 
91
                }, {
 
92
                        args: []string{"new-model"},
 
93
                        name: "new-model",
 
94
                }, {
 
95
                        args: []string{"n"},
 
96
                        name: "n",
 
97
                }, {
 
98
                        args: []string{"new model"},
 
99
                        err:  fmt.Sprintf(modelNameErr, "new model"),
 
100
                }, {
 
101
                        args: []string{"newModel"},
 
102
                        err:  fmt.Sprintf(modelNameErr, "newModel"),
 
103
                }, {
 
104
                        args: []string{"-"},
 
105
                        err:  fmt.Sprintf(modelNameErr, "-"),
 
106
                }, {
 
107
                        args: []string{"new@model"},
 
108
                        err:  fmt.Sprintf(modelNameErr, "new@model"),
 
109
                }, {
 
110
                        args:  []string{"new-model", "--owner", "foo"},
 
111
                        name:  "new-model",
 
112
                        owner: "foo",
 
113
                }, {
 
114
                        args: []string{"new-model", "--owner", "not=valid"},
 
115
                        err:  `"not=valid" is not a valid user`,
 
116
                }, {
 
117
                        args:   []string{"new-model", "--config", "key=value", "--config", "key2=value2"},
 
118
                        name:   "new-model",
 
119
                        values: map[string]interface{}{"key": "value", "key2": "value2"},
 
120
                }, {
 
121
                        args: []string{"new-model", "extra", "args"},
 
122
                        err:  `unrecognized args: \["extra" "args"\]`,
 
123
                },
 
124
        } {
 
125
                c.Logf("test %d", i)
 
126
                wrappedCommand, command := controller.NewAddModelCommandForTest(nil, nil, nil, s.store)
 
127
                err := testing.InitCommand(wrappedCommand, test.args)
 
128
                if test.err != "" {
 
129
                        c.Assert(err, gc.ErrorMatches, test.err)
 
130
                        continue
 
131
                }
 
132
 
 
133
                c.Assert(err, jc.ErrorIsNil)
 
134
                c.Assert(command.Name, gc.Equals, test.name)
 
135
                c.Assert(command.Owner, gc.Equals, test.owner)
 
136
                attrs, err := command.Config.ReadAttrs(nil)
 
137
                c.Assert(err, jc.ErrorIsNil)
 
138
                if len(test.values) == 0 {
 
139
                        c.Assert(attrs, gc.HasLen, 0)
 
140
                } else {
 
141
                        c.Assert(attrs, jc.DeepEquals, test.values)
 
142
                }
 
143
        }
 
144
}
 
145
 
 
146
func (s *addSuite) TestAddExistingName(c *gc.C) {
 
147
        // If there's any model details existing, we just overwrite them. The
 
148
        // controller will error out if the model already exists. Overwriting
 
149
        // means we'll replace any stale details from an previously existing
 
150
        // model with the same name.
 
151
        err := s.store.UpdateModel("test-master", "bob@local/test", jujuclient.ModelDetails{
 
152
                "stale-uuid",
 
153
        })
 
154
        c.Assert(err, jc.ErrorIsNil)
 
155
 
 
156
        _, err = s.run(c, "test")
 
157
        c.Assert(err, jc.ErrorIsNil)
 
158
 
 
159
        details, err := s.store.ModelByName("test-master", "bob@local/test")
 
160
        c.Assert(err, jc.ErrorIsNil)
 
161
        c.Assert(details, jc.DeepEquals, &jujuclient.ModelDetails{"fake-model-uuid"})
 
162
}
 
163
 
 
164
func (s *addSuite) TestCredentialsPassedThrough(c *gc.C) {
 
165
        c.Skip("TODO(wallyworld) - port to using new credential management")
 
166
        _, err := s.run(c, "test", "--credential", "secrets")
 
167
        c.Assert(err, jc.ErrorIsNil)
 
168
 
 
169
        c.Assert(s.fakeAddModelAPI.cloudCredential, gc.Equals, "secrets")
 
170
        c.Assert(s.fakeAddModelAPI.config["type"], gc.Equals, "ec2")
 
171
}
 
172
 
 
173
func (s *addSuite) TestComandLineConfigPassedThrough(c *gc.C) {
 
174
        _, err := s.run(c, "test", "--config", "account=magic", "--config", "cloud=special")
 
175
        c.Assert(err, jc.ErrorIsNil)
 
176
 
 
177
        c.Assert(s.fakeAddModelAPI.config["account"], gc.Equals, "magic")
 
178
        c.Assert(s.fakeAddModelAPI.config["cloud"], gc.Equals, "special")
 
179
}
 
180
 
 
181
func (s *addSuite) TestConfigFileValuesPassedThrough(c *gc.C) {
 
182
        config := map[string]string{
 
183
                "account": "magic",
 
184
                "cloud":   "9",
 
185
        }
 
186
        bytes, err := yaml.Marshal(config)
 
187
        c.Assert(err, jc.ErrorIsNil)
 
188
        file, err := ioutil.TempFile(c.MkDir(), "")
 
189
        c.Assert(err, jc.ErrorIsNil)
 
190
        file.Write(bytes)
 
191
        file.Close()
 
192
 
 
193
        _, err = s.run(c, "test", "--config", file.Name())
 
194
        c.Assert(err, jc.ErrorIsNil)
 
195
        c.Assert(s.fakeAddModelAPI.config["account"], gc.Equals, "magic")
 
196
        c.Assert(s.fakeAddModelAPI.config["cloud"], gc.Equals, "9")
 
197
}
 
198
 
 
199
func (s *addSuite) TestConfigFileWithNestedMaps(c *gc.C) {
 
200
        nestedConfig := map[string]interface{}{
 
201
                "account": "magic",
 
202
                "cloud":   "9",
 
203
        }
 
204
        config := map[string]interface{}{
 
205
                "foo":    "bar",
 
206
                "nested": nestedConfig,
 
207
        }
 
208
 
 
209
        bytes, err := yaml.Marshal(config)
 
210
        c.Assert(err, jc.ErrorIsNil)
 
211
        file, err := ioutil.TempFile(c.MkDir(), "")
 
212
        c.Assert(err, jc.ErrorIsNil)
 
213
        file.Write(bytes)
 
214
        file.Close()
 
215
 
 
216
        _, err = s.run(c, "test", "--config", file.Name())
 
217
        c.Assert(err, jc.ErrorIsNil)
 
218
        c.Assert(s.fakeAddModelAPI.config["foo"], gc.Equals, "bar")
 
219
        c.Assert(s.fakeAddModelAPI.config["nested"], jc.DeepEquals, nestedConfig)
 
220
}
 
221
 
 
222
func (s *addSuite) TestConfigFileFailsToConform(c *gc.C) {
 
223
        nestedConfig := map[int]interface{}{
 
224
                9: "9",
 
225
        }
 
226
        config := map[string]interface{}{
 
227
                "foo":    "bar",
 
228
                "nested": nestedConfig,
 
229
        }
 
230
        bytes, err := yaml.Marshal(config)
 
231
        c.Assert(err, jc.ErrorIsNil)
 
232
        file, err := ioutil.TempFile(c.MkDir(), "")
 
233
        c.Assert(err, jc.ErrorIsNil)
 
234
        file.Write(bytes)
 
235
        file.Close()
 
236
 
 
237
        _, err = s.run(c, "test", "--config", file.Name())
 
238
        c.Assert(err, gc.ErrorMatches, `unable to parse config: map keyed with non-string value`)
 
239
}
 
240
 
 
241
func (s *addSuite) TestConfigFileFormatError(c *gc.C) {
 
242
        file, err := ioutil.TempFile(c.MkDir(), "")
 
243
        c.Assert(err, jc.ErrorIsNil)
 
244
        file.Write(([]byte)("not: valid: yaml"))
 
245
        file.Close()
 
246
 
 
247
        _, err = s.run(c, "test", "--config", file.Name())
 
248
        c.Assert(err, gc.ErrorMatches, `unable to parse config: yaml: .*`)
 
249
}
 
250
 
 
251
func (s *addSuite) TestConfigFileDoesntExist(c *gc.C) {
 
252
        _, err := s.run(c, "test", "--config", "missing-file")
 
253
        errMsg := ".*" + utils.NoSuchFileErrRegexp
 
254
        c.Assert(err, gc.ErrorMatches, errMsg)
 
255
}
 
256
 
 
257
func (s *addSuite) TestConfigValuePrecedence(c *gc.C) {
 
258
        config := map[string]string{
 
259
                "account": "magic",
 
260
                "cloud":   "9",
 
261
        }
 
262
        bytes, err := yaml.Marshal(config)
 
263
        c.Assert(err, jc.ErrorIsNil)
 
264
        file, err := ioutil.TempFile(c.MkDir(), "")
 
265
        c.Assert(err, jc.ErrorIsNil)
 
266
        file.Write(bytes)
 
267
        file.Close()
 
268
 
 
269
        _, err = s.run(c, "test", "--config", file.Name(), "--config", "account=magic", "--config", "cloud=special")
 
270
        c.Assert(err, jc.ErrorIsNil)
 
271
        c.Assert(s.fakeAddModelAPI.config["account"], gc.Equals, "magic")
 
272
        c.Assert(s.fakeAddModelAPI.config["cloud"], gc.Equals, "special")
 
273
}
 
274
 
 
275
func (s *addSuite) TestAddErrorRemoveConfigstoreInfo(c *gc.C) {
 
276
        s.fakeAddModelAPI.err = errors.New("bah humbug")
 
277
 
 
278
        _, err := s.run(c, "test")
 
279
        c.Assert(err, gc.ErrorMatches, "bah humbug")
 
280
 
 
281
        _, err = s.store.ModelByName("test-master", "bob@local/test")
 
282
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
 
283
}
 
284
 
 
285
func (s *addSuite) TestAddStoresValues(c *gc.C) {
 
286
        _, err := s.run(c, "test")
 
287
        c.Assert(err, jc.ErrorIsNil)
 
288
 
 
289
        model, err := s.store.ModelByName("test-master", "bob@local/test")
 
290
        c.Assert(err, jc.ErrorIsNil)
 
291
        c.Assert(model, jc.DeepEquals, &jujuclient.ModelDetails{"fake-model-uuid"})
 
292
}
 
293
 
 
294
func (s *addSuite) TestNoEnvCacheOtherUser(c *gc.C) {
 
295
        _, err := s.run(c, "test", "--owner", "zeus")
 
296
        c.Assert(err, jc.ErrorIsNil)
 
297
 
 
298
        // Creating a model for another user does not update the model cache.
 
299
        _, err = s.store.ModelByName("test-master", "bob@local/test")
 
300
        c.Assert(err, jc.Satisfies, errors.IsNotFound)
 
301
}
 
302
 
 
303
// fakeAddClient is used to mock out the behavior of the real
 
304
// AddModel command.
 
305
type fakeAddClient struct {
 
306
        owner           string
 
307
        cloudRegion     string
 
308
        cloudCredential string
 
309
        config          map[string]interface{}
 
310
        err             error
 
311
        model           params.ModelInfo
 
312
}
 
313
 
 
314
var _ controller.AddModelAPI = (*fakeAddClient)(nil)
 
315
 
 
316
func (*fakeAddClient) Close() error {
 
317
        return nil
 
318
}
 
319
 
 
320
func (f *fakeAddClient) CreateModel(name, owner, cloudRegion, cloudCredential string, config map[string]interface{}) (params.ModelInfo, error) {
 
321
        if f.err != nil {
 
322
                return params.ModelInfo{}, f.err
 
323
        }
 
324
        f.owner = owner
 
325
        f.cloudCredential = cloudCredential
 
326
        f.cloudRegion = cloudRegion
 
327
        f.config = config
 
328
        return f.model, nil
 
329
}
 
330
 
 
331
// TODO(wallyworld) - improve this stub and add test asserts
 
332
type fakeCloudAPI struct {
 
333
        controller.CloudAPI
 
334
}
 
335
 
 
336
func (c *fakeCloudAPI) Credentials(names.UserTag, names.CloudTag) (map[string]cloud.Credential, error) {
 
337
        return map[string]cloud.Credential{
 
338
                "default": cloud.NewEmptyCredential(),
 
339
        }, nil
 
340
}
 
341
 
 
342
func (c *fakeCloudAPI) UpdateCredentials(names.UserTag, names.CloudTag, map[string]cloud.Credential) error {
 
343
        return nil
 
344
}