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