github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/cmd/juju/system/createenvironment_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package system_test 5 6 import ( 7 "io/ioutil" 8 "os" 9 "os/user" 10 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/yaml.v1" 17 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/cmd/envcmd" 20 "github.com/juju/juju/cmd/juju/system" 21 "github.com/juju/juju/environs/configstore" 22 "github.com/juju/juju/feature" 23 "github.com/juju/juju/testing" 24 ) 25 26 type createSuite struct { 27 testing.FakeJujuHomeSuite 28 fake *fakeCreateClient 29 parser func(interface{}) (interface{}, error) 30 store configstore.Storage 31 serverUUID string 32 server configstore.EnvironInfo 33 } 34 35 var _ = gc.Suite(&createSuite{}) 36 37 func (s *createSuite) SetUpTest(c *gc.C) { 38 s.FakeJujuHomeSuite.SetUpTest(c) 39 s.SetFeatureFlags(feature.JES) 40 s.fake = &fakeCreateClient{} 41 s.parser = nil 42 store := configstore.Default 43 s.AddCleanup(func(*gc.C) { 44 configstore.Default = store 45 }) 46 s.store = configstore.NewMem() 47 configstore.Default = func() (configstore.Storage, error) { 48 return s.store, nil 49 } 50 // Set up the current environment, and write just enough info 51 // so we don't try to refresh 52 envName := "test-master" 53 s.serverUUID = "fake-server-uuid" 54 info := s.store.CreateInfo(envName) 55 info.SetAPIEndpoint(configstore.APIEndpoint{ 56 Addresses: []string{"localhost"}, 57 CACert: testing.CACert, 58 EnvironUUID: s.serverUUID, 59 ServerUUID: s.serverUUID, 60 }) 61 info.SetAPICredentials(configstore.APICredentials{User: "bob", Password: "sekrit"}) 62 err := info.Write() 63 c.Assert(err, jc.ErrorIsNil) 64 s.server = info 65 err = envcmd.WriteCurrentEnvironment(envName) 66 c.Assert(err, jc.ErrorIsNil) 67 } 68 69 func (s *createSuite) run(c *gc.C, args ...string) (*cmd.Context, error) { 70 command := system.NewCreateEnvironmentCommand(s.fake, s.parser) 71 return testing.RunCommand(c, envcmd.WrapSystem(command), args...) 72 } 73 74 func (s *createSuite) TestInit(c *gc.C) { 75 76 for i, test := range []struct { 77 args []string 78 err string 79 name string 80 owner string 81 path string 82 values map[string]string 83 }{ 84 { 85 err: "environment name is required", 86 }, { 87 args: []string{"new-env"}, 88 name: "new-env", 89 }, { 90 args: []string{"new-env", "--owner", "foo"}, 91 name: "new-env", 92 owner: "foo", 93 }, { 94 args: []string{"new-env", "--owner", "not=valid"}, 95 err: `"not=valid" is not a valid user`, 96 }, { 97 args: []string{"new-env", "key=value", "key2=value2"}, 98 name: "new-env", 99 values: map[string]string{"key": "value", "key2": "value2"}, 100 }, { 101 args: []string{"new-env", "key=value", "key=value2"}, 102 err: `key "key" specified more than once`, 103 }, { 104 args: []string{"new-env", "another"}, 105 err: `expected "key=value", got "another"`, 106 }, { 107 args: []string{"new-env", "--config", "some-file"}, 108 name: "new-env", 109 path: "some-file", 110 }, 111 } { 112 c.Logf("test %d", i) 113 create := &system.CreateEnvironmentCommand{} 114 err := testing.InitCommand(create, test.args) 115 if test.err != "" { 116 c.Assert(err, gc.ErrorMatches, test.err) 117 continue 118 } 119 120 c.Assert(err, jc.ErrorIsNil) 121 c.Assert(create.Name(), gc.Equals, test.name) 122 c.Assert(create.Owner(), gc.Equals, test.owner) 123 c.Assert(create.ConfigFile().Path, gc.Equals, test.path) 124 // The config value parse method returns an empty map 125 // if there were no values 126 if len(test.values) == 0 { 127 c.Assert(create.ConfValues(), gc.HasLen, 0) 128 } else { 129 c.Assert(create.ConfValues(), jc.DeepEquals, test.values) 130 } 131 } 132 } 133 134 func (s *createSuite) TestCreateExistingName(c *gc.C) { 135 // Make a configstore entry with the same name. 136 info := s.store.CreateInfo("test") 137 err := info.Write() 138 c.Assert(err, jc.ErrorIsNil) 139 140 _, err = s.run(c, "test") 141 c.Assert(err, gc.ErrorMatches, `environment "test" already exists`) 142 } 143 144 func (s *createSuite) TestComandLineConfigPassedThrough(c *gc.C) { 145 _, err := s.run(c, "test", "account=magic", "cloud=special") 146 c.Assert(err, jc.ErrorIsNil) 147 148 c.Assert(s.fake.config["account"], gc.Equals, "magic") 149 c.Assert(s.fake.config["cloud"], gc.Equals, "special") 150 } 151 152 func (s *createSuite) TestConfigFileValuesPassedThrough(c *gc.C) { 153 config := map[string]string{ 154 "account": "magic", 155 "cloud": "9", 156 } 157 bytes, err := yaml.Marshal(config) 158 c.Assert(err, jc.ErrorIsNil) 159 file, err := ioutil.TempFile(c.MkDir(), "") 160 c.Assert(err, jc.ErrorIsNil) 161 file.Write(bytes) 162 file.Close() 163 164 _, err = s.run(c, "test", "--config", file.Name()) 165 c.Assert(err, jc.ErrorIsNil) 166 c.Assert(s.fake.config["account"], gc.Equals, "magic") 167 c.Assert(s.fake.config["cloud"], gc.Equals, "9") 168 } 169 170 func (s *createSuite) TestConfigFileWithNestedMaps(c *gc.C) { 171 nestedConfig := map[string]interface{}{ 172 "account": "magic", 173 "cloud": "9", 174 } 175 config := map[string]interface{}{ 176 "foo": "bar", 177 "nested": nestedConfig, 178 } 179 180 bytes, err := yaml.Marshal(config) 181 c.Assert(err, jc.ErrorIsNil) 182 file, err := ioutil.TempFile(c.MkDir(), "") 183 c.Assert(err, jc.ErrorIsNil) 184 file.Write(bytes) 185 file.Close() 186 187 _, err = s.run(c, "test", "--config", file.Name()) 188 c.Assert(err, jc.ErrorIsNil) 189 c.Assert(s.fake.config["foo"], gc.Equals, "bar") 190 c.Assert(s.fake.config["nested"], jc.DeepEquals, nestedConfig) 191 } 192 193 func (s *createSuite) TestConfigFileFailsToConform(c *gc.C) { 194 nestedConfig := map[int]interface{}{ 195 9: "9", 196 } 197 config := map[string]interface{}{ 198 "foo": "bar", 199 "nested": nestedConfig, 200 } 201 bytes, err := yaml.Marshal(config) 202 c.Assert(err, jc.ErrorIsNil) 203 file, err := ioutil.TempFile(c.MkDir(), "") 204 c.Assert(err, jc.ErrorIsNil) 205 file.Write(bytes) 206 file.Close() 207 208 _, err = s.run(c, "test", "--config", file.Name()) 209 c.Assert(err, gc.ErrorMatches, `unable to parse config file: map keyed with non-string value`) 210 } 211 212 func (s *createSuite) TestConfigFileFailsWithUnknownType(c *gc.C) { 213 config := map[string]interface{}{ 214 "account": "magic", 215 "cloud": "9", 216 } 217 218 bytes, err := yaml.Marshal(config) 219 c.Assert(err, jc.ErrorIsNil) 220 file, err := ioutil.TempFile(c.MkDir(), "") 221 c.Assert(err, jc.ErrorIsNil) 222 file.Write(bytes) 223 file.Close() 224 225 s.parser = func(interface{}) (interface{}, error) { return "not a map", nil } 226 _, err = s.run(c, "test", "--config", file.Name()) 227 c.Assert(err, gc.ErrorMatches, `config must contain a YAML map with string keys`) 228 } 229 230 func (s *createSuite) TestConfigFileFormatError(c *gc.C) { 231 file, err := ioutil.TempFile(c.MkDir(), "") 232 c.Assert(err, jc.ErrorIsNil) 233 file.Write(([]byte)("not: valid: yaml")) 234 file.Close() 235 236 _, err = s.run(c, "test", "--config", file.Name()) 237 c.Assert(err, gc.ErrorMatches, `unable to parse config file: YAML error: .*`) 238 } 239 240 func (s *createSuite) TestConfigFileDoesntExist(c *gc.C) { 241 _, err := s.run(c, "test", "--config", "missing-file") 242 errMsg := ".*" + utils.NoSuchFileErrRegexp 243 c.Assert(err, gc.ErrorMatches, errMsg) 244 } 245 246 func (s *createSuite) TestConfigValuePrecedence(c *gc.C) { 247 config := map[string]string{ 248 "account": "magic", 249 "cloud": "9", 250 } 251 bytes, err := yaml.Marshal(config) 252 c.Assert(err, jc.ErrorIsNil) 253 file, err := ioutil.TempFile(c.MkDir(), "") 254 c.Assert(err, jc.ErrorIsNil) 255 file.Write(bytes) 256 file.Close() 257 258 _, err = s.run(c, "test", "--config", file.Name(), "account=magic", "cloud=special") 259 c.Assert(err, jc.ErrorIsNil) 260 c.Assert(s.fake.config["account"], gc.Equals, "magic") 261 c.Assert(s.fake.config["cloud"], gc.Equals, "special") 262 } 263 264 var setConfigSpecialCaseDefaultsTests = []struct { 265 about string 266 userEnvVar string 267 userCurrent func() (*user.User, error) 268 config map[string]interface{} 269 expectConfig map[string]interface{} 270 expectError string 271 }{{ 272 about: "use env var if available", 273 userEnvVar: "bob", 274 config: map[string]interface{}{ 275 "name": "envname", 276 "type": "local", 277 }, 278 expectConfig: map[string]interface{}{ 279 "name": "envname", 280 "type": "local", 281 "namespace": "bob-envname", 282 }, 283 }, { 284 about: "fall back to user.Current", 285 userCurrent: func() (*user.User, error) { 286 return &user.User{Username: "bob"}, nil 287 }, 288 config: map[string]interface{}{ 289 "name": "envname", 290 "type": "local", 291 }, 292 expectConfig: map[string]interface{}{ 293 "name": "envname", 294 "type": "local", 295 "namespace": "bob-envname", 296 }, 297 }, { 298 about: "other provider types unaffected", 299 userEnvVar: "bob", 300 config: map[string]interface{}{ 301 "name": "envname", 302 "type": "dummy", 303 }, 304 expectConfig: map[string]interface{}{ 305 "name": "envname", 306 "type": "dummy", 307 }, 308 }, { 309 about: "explicit namespace takes precedence", 310 userCurrent: func() (*user.User, error) { 311 return &user.User{Username: "bob"}, nil 312 }, 313 config: map[string]interface{}{ 314 "name": "envname", 315 "namespace": "something", 316 "type": "local", 317 }, 318 expectConfig: map[string]interface{}{ 319 "name": "envname", 320 "namespace": "something", 321 "type": "local", 322 }, 323 }, { 324 about: "user.Current returns error", 325 userCurrent: func() (*user.User, error) { 326 return nil, errors.New("an error") 327 }, 328 config: map[string]interface{}{ 329 "name": "envname", 330 "type": "local", 331 }, 332 expectError: "failed to determine username for namespace: an error", 333 }} 334 335 func (s *createSuite) TestSetConfigSpecialCaseDefaults(c *gc.C) { 336 noUserCurrent := func() (*user.User, error) { 337 panic("should not be called") 338 } 339 s.PatchValue(system.UserCurrent, noUserCurrent) 340 // We test setConfigSpecialCaseDefaults independently 341 // because we can't use the local provider in the tests. 342 for i, test := range setConfigSpecialCaseDefaultsTests { 343 c.Logf("test %d: %s", i, test.about) 344 os.Setenv("USER", test.userEnvVar) 345 if test.userCurrent != nil { 346 *system.UserCurrent = test.userCurrent 347 } else { 348 *system.UserCurrent = noUserCurrent 349 } 350 err := system.SetConfigSpecialCaseDefaults(test.config["name"].(string), test.config) 351 if test.expectError != "" { 352 c.Assert(err, gc.ErrorMatches, test.expectError) 353 } else { 354 c.Assert(err, gc.IsNil) 355 c.Assert(test.config, jc.DeepEquals, test.expectConfig) 356 } 357 } 358 359 } 360 361 func (s *createSuite) TestCreateErrorRemoveConfigstoreInfo(c *gc.C) { 362 s.fake.err = errors.New("bah humbug") 363 364 _, err := s.run(c, "test") 365 c.Assert(err, gc.ErrorMatches, "bah humbug") 366 367 _, err = s.store.ReadInfo("test") 368 c.Assert(err, gc.ErrorMatches, `environment "test" not found`) 369 } 370 371 func (s *createSuite) TestCreateStoresValues(c *gc.C) { 372 s.fake.env = params.Environment{ 373 Name: "test", 374 UUID: "fake-env-uuid", 375 OwnerTag: "ignored-for-now", 376 ServerUUID: s.serverUUID, 377 } 378 _, err := s.run(c, "test") 379 c.Assert(err, jc.ErrorIsNil) 380 381 info, err := s.store.ReadInfo("test") 382 c.Assert(err, jc.ErrorIsNil) 383 // Stores the credentials of the original environment 384 c.Assert(info.APICredentials(), jc.DeepEquals, s.server.APICredentials()) 385 endpoint := info.APIEndpoint() 386 expected := s.server.APIEndpoint() 387 c.Assert(endpoint.Addresses, jc.DeepEquals, expected.Addresses) 388 c.Assert(endpoint.Hostnames, jc.DeepEquals, expected.Hostnames) 389 c.Assert(endpoint.ServerUUID, gc.Equals, expected.ServerUUID) 390 c.Assert(endpoint.CACert, gc.Equals, expected.CACert) 391 c.Assert(endpoint.EnvironUUID, gc.Equals, "fake-env-uuid") 392 } 393 394 func (s *createSuite) TestNoEnvCacheOtherUser(c *gc.C) { 395 s.fake.env = params.Environment{ 396 Name: "test", 397 UUID: "fake-env-uuid", 398 OwnerTag: "ignored-for-now", 399 ServerUUID: s.serverUUID, 400 } 401 _, err := s.run(c, "test", "--owner", "zeus") 402 c.Assert(err, jc.ErrorIsNil) 403 404 _, err = s.store.ReadInfo("test") 405 c.Assert(err, gc.ErrorMatches, `environment "test" not found`) 406 } 407 408 // fakeCreateClient is used to mock out the behavior of the real 409 // CreateEnvironment command. 410 type fakeCreateClient struct { 411 owner string 412 account map[string]interface{} 413 config map[string]interface{} 414 err error 415 env params.Environment 416 } 417 418 var _ system.CreateEnvironmentAPI = (*fakeCreateClient)(nil) 419 420 func (*fakeCreateClient) Close() error { 421 return nil 422 } 423 424 func (*fakeCreateClient) ConfigSkeleton(provider, region string) (params.EnvironConfig, error) { 425 return params.EnvironConfig{ 426 "type": "dummy", 427 "state-server": false, 428 }, nil 429 } 430 func (f *fakeCreateClient) CreateEnvironment(owner string, account, config map[string]interface{}) (params.Environment, error) { 431 var env params.Environment 432 if f.err != nil { 433 return env, f.err 434 } 435 f.owner = owner 436 f.account = account 437 f.config = config 438 return f.env, nil 439 }