github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/initialize_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 gitjujutesting "github.com/juju/testing" 8 jc "github.com/juju/testing/checkers" 9 "github.com/juju/utils/clock" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/cloud" 14 "github.com/juju/juju/constraints" 15 "github.com/juju/juju/controller" 16 "github.com/juju/juju/environs" 17 "github.com/juju/juju/environs/config" 18 "github.com/juju/juju/mongo/mongotest" 19 "github.com/juju/juju/state" 20 statetesting "github.com/juju/juju/state/testing" 21 "github.com/juju/juju/storage" 22 "github.com/juju/juju/testing" 23 ) 24 25 type InitializeSuite struct { 26 gitjujutesting.MgoSuite 27 testing.BaseSuite 28 State *state.State 29 } 30 31 var _ = gc.Suite(&InitializeSuite{}) 32 33 func (s *InitializeSuite) SetUpSuite(c *gc.C) { 34 s.BaseSuite.SetUpSuite(c) 35 s.MgoSuite.SetUpSuite(c) 36 } 37 38 func (s *InitializeSuite) TearDownSuite(c *gc.C) { 39 s.MgoSuite.TearDownSuite(c) 40 s.BaseSuite.TearDownSuite(c) 41 } 42 43 func (s *InitializeSuite) SetUpTest(c *gc.C) { 44 s.BaseSuite.SetUpTest(c) 45 s.MgoSuite.SetUpTest(c) 46 } 47 48 func (s *InitializeSuite) openState(c *gc.C, modelTag names.ModelTag) { 49 st, err := state.Open( 50 modelTag, 51 testing.ControllerTag, 52 statetesting.NewMongoInfo(), 53 mongotest.DialOpts(), 54 state.NewPolicyFunc(nil), 55 ) 56 c.Assert(err, jc.ErrorIsNil) 57 s.State = st 58 } 59 60 func (s *InitializeSuite) TearDownTest(c *gc.C) { 61 if s.State != nil { 62 err := s.State.Close() 63 c.Check(err, jc.ErrorIsNil) 64 } 65 s.MgoSuite.TearDownTest(c) 66 s.BaseSuite.TearDownTest(c) 67 } 68 69 func (s *InitializeSuite) TestInitialize(c *gc.C) { 70 cfg := testing.ModelConfig(c) 71 uuid := cfg.UUID() 72 owner := names.NewLocalUserTag("initialize-admin") 73 74 userPassCredentialTag := names.NewCloudCredentialTag( 75 "dummy/" + owner.Canonical() + "/some-credential", 76 ) 77 emptyCredentialTag := names.NewCloudCredentialTag( 78 "dummy/" + owner.Canonical() + "/empty-credential", 79 ) 80 userpassCredential := cloud.NewCredential( 81 cloud.UserPassAuthType, 82 map[string]string{ 83 "username": "alice", 84 "password": "hunter2", 85 }, 86 ) 87 userpassCredential.Label = userPassCredentialTag.Name() 88 emptyCredential := cloud.NewEmptyCredential() 89 emptyCredential.Label = emptyCredentialTag.Name() 90 cloudCredentialsIn := map[names.CloudCredentialTag]cloud.Credential{ 91 userPassCredentialTag: userpassCredential, 92 emptyCredentialTag: emptyCredential, 93 } 94 controllerCfg := testing.FakeControllerConfig() 95 96 st, err := state.Initialize(state.InitializeParams{ 97 Clock: clock.WallClock, 98 ControllerConfig: controllerCfg, 99 ControllerModelArgs: state.ModelArgs{ 100 Owner: owner, 101 Config: cfg, 102 CloudName: "dummy", 103 CloudRegion: "dummy-region", 104 CloudCredential: userPassCredentialTag, 105 StorageProviderRegistry: storage.StaticProviderRegistry{}, 106 }, 107 CloudName: "dummy", 108 Cloud: cloud.Cloud{ 109 Type: "dummy", 110 AuthTypes: []cloud.AuthType{ 111 cloud.EmptyAuthType, cloud.UserPassAuthType, 112 }, 113 Regions: []cloud.Region{{Name: "dummy-region"}}, 114 }, 115 CloudCredentials: cloudCredentialsIn, 116 MongoInfo: statetesting.NewMongoInfo(), 117 MongoDialOpts: mongotest.DialOpts(), 118 }) 119 c.Assert(err, jc.ErrorIsNil) 120 c.Assert(st, gc.NotNil) 121 modelTag := st.ModelTag() 122 c.Assert(modelTag.Id(), gc.Equals, uuid) 123 err = st.Close() 124 c.Assert(err, jc.ErrorIsNil) 125 126 s.openState(c, modelTag) 127 128 cfg, err = s.State.ModelConfig() 129 c.Assert(err, jc.ErrorIsNil) 130 expected := cfg.AllAttrs() 131 for k, v := range config.ConfigDefaults() { 132 if _, ok := expected[k]; !ok { 133 expected[k] = v 134 } 135 } 136 c.Assert(cfg.AllAttrs(), jc.DeepEquals, expected) 137 // Check that the model has been created. 138 model, err := s.State.Model() 139 c.Assert(err, jc.ErrorIsNil) 140 c.Assert(model.Tag(), gc.Equals, modelTag) 141 c.Assert(model.CloudRegion(), gc.Equals, "dummy-region") 142 // Check that the owner has been created. 143 c.Assert(model.Owner(), gc.Equals, owner) 144 // Check that the owner can be retrieved by the tag. 145 entity, err := s.State.FindEntity(model.Owner()) 146 c.Assert(err, jc.ErrorIsNil) 147 c.Assert(entity.Tag(), gc.Equals, owner) 148 // Check that the owner has an ModelUser created for the bootstrapped model. 149 modelUser, err := s.State.UserAccess(model.Owner(), model.Tag()) 150 c.Assert(err, jc.ErrorIsNil) 151 c.Assert(modelUser.UserTag, gc.Equals, owner) 152 c.Assert(modelUser.Object, gc.Equals, model.Tag()) 153 154 // Check that the model can be found through the tag. 155 entity, err = s.State.FindEntity(modelTag) 156 c.Assert(err, jc.ErrorIsNil) 157 cons, err := s.State.ModelConstraints() 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(&cons, jc.Satisfies, constraints.IsEmpty) 160 161 addrs, err := s.State.APIHostPorts() 162 c.Assert(err, jc.ErrorIsNil) 163 c.Assert(addrs, gc.HasLen, 0) 164 165 info, err := s.State.ControllerInfo() 166 c.Assert(err, jc.ErrorIsNil) 167 c.Assert(info, jc.DeepEquals, &state.ControllerInfo{ModelTag: modelTag, CloudName: "dummy"}) 168 169 // Check that the model's cloud and credential names are as 170 // expected, and the owner's cloud credentials are initialised. 171 c.Assert(model.Cloud(), gc.Equals, "dummy") 172 credentialTag, ok := model.CloudCredential() 173 c.Assert(ok, jc.IsTrue) 174 c.Assert(credentialTag, gc.Equals, userPassCredentialTag) 175 cloudCredentials, err := s.State.CloudCredentials(model.Owner(), "dummy") 176 c.Assert(err, jc.ErrorIsNil) 177 expectedCred := make(map[string]cloud.Credential, len(cloudCredentialsIn)) 178 for tag, cred := range cloudCredentialsIn { 179 expectedCred[tag.Canonical()] = cred 180 } 181 c.Assert(cloudCredentials, jc.DeepEquals, expectedCred) 182 } 183 184 func (s *InitializeSuite) TestInitializeWithInvalidCredentialType(c *gc.C) { 185 owner := names.NewLocalUserTag("initialize-admin") 186 modelCfg := testing.ModelConfig(c) 187 controllerCfg := testing.FakeControllerConfig() 188 credentialTag := names.NewCloudCredentialTag("dummy/" + owner.Canonical() + "/borken") 189 _, err := state.Initialize(state.InitializeParams{ 190 Clock: clock.WallClock, 191 ControllerConfig: controllerCfg, 192 ControllerModelArgs: state.ModelArgs{ 193 CloudName: "dummy", 194 Owner: owner, 195 Config: modelCfg, 196 StorageProviderRegistry: storage.StaticProviderRegistry{}, 197 }, 198 CloudName: "dummy", 199 Cloud: cloud.Cloud{ 200 Type: "dummy", 201 AuthTypes: []cloud.AuthType{ 202 cloud.AccessKeyAuthType, cloud.OAuth1AuthType, 203 }, 204 }, 205 CloudCredentials: map[names.CloudCredentialTag]cloud.Credential{ 206 credentialTag: cloud.NewCredential(cloud.UserPassAuthType, nil), 207 }, 208 MongoInfo: statetesting.NewMongoInfo(), 209 MongoDialOpts: mongotest.DialOpts(), 210 }) 211 c.Assert(err, gc.ErrorMatches, 212 `validating initialization args: validating cloud credentials: credential "dummy/initialize-admin@local/borken" with auth-type "userpass" is not supported \(expected one of \["access-key" "oauth1"\]\)`, 213 ) 214 } 215 216 func (s *InitializeSuite) TestInitializeWithControllerInheritedConfig(c *gc.C) { 217 cfg := testing.ModelConfig(c) 218 uuid := cfg.UUID() 219 initial := cfg.AllAttrs() 220 controllerInheritedConfigIn := map[string]interface{}{ 221 "default-series": initial["default-series"], 222 } 223 owner := names.NewLocalUserTag("initialize-admin") 224 controllerCfg := testing.FakeControllerConfig() 225 226 st, err := state.Initialize(state.InitializeParams{ 227 Clock: clock.WallClock, 228 ControllerConfig: controllerCfg, 229 ControllerModelArgs: state.ModelArgs{ 230 CloudName: "dummy", 231 Owner: owner, 232 Config: cfg, 233 StorageProviderRegistry: storage.StaticProviderRegistry{}, 234 }, 235 CloudName: "dummy", 236 Cloud: cloud.Cloud{ 237 Type: "dummy", 238 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 239 }, 240 ControllerInheritedConfig: controllerInheritedConfigIn, 241 MongoInfo: statetesting.NewMongoInfo(), 242 MongoDialOpts: mongotest.DialOpts(), 243 }) 244 c.Assert(err, jc.ErrorIsNil) 245 c.Assert(st, gc.NotNil) 246 modelTag := st.ModelTag() 247 c.Assert(modelTag.Id(), gc.Equals, uuid) 248 err = st.Close() 249 c.Assert(err, jc.ErrorIsNil) 250 251 s.openState(c, modelTag) 252 253 controllerInheritedConfig, err := state.ReadSettings(s.State, state.GlobalSettingsC, state.ControllerInheritedSettingsGlobalKey) 254 c.Assert(err, jc.ErrorIsNil) 255 c.Assert(controllerInheritedConfig.Map(), jc.DeepEquals, controllerInheritedConfigIn) 256 257 expected := cfg.AllAttrs() 258 for k, v := range config.ConfigDefaults() { 259 if _, ok := expected[k]; !ok { 260 expected[k] = v 261 } 262 } 263 // Config as read from state has resources tags coerced to a map. 264 expected["resource-tags"] = map[string]string{} 265 cfg, err = s.State.ModelConfig() 266 c.Assert(err, jc.ErrorIsNil) 267 c.Assert(cfg.AllAttrs(), jc.DeepEquals, expected) 268 } 269 270 func (s *InitializeSuite) TestDoubleInitializeConfig(c *gc.C) { 271 cfg := testing.ModelConfig(c) 272 owner := names.NewLocalUserTag("initialize-admin") 273 274 mgoInfo := statetesting.NewMongoInfo() 275 dialOpts := mongotest.DialOpts() 276 controllerCfg := testing.FakeControllerConfig() 277 278 args := state.InitializeParams{ 279 Clock: clock.WallClock, 280 ControllerConfig: controllerCfg, 281 ControllerModelArgs: state.ModelArgs{ 282 CloudName: "dummy", 283 Owner: owner, 284 Config: cfg, 285 StorageProviderRegistry: storage.StaticProviderRegistry{}, 286 }, 287 CloudName: "dummy", 288 Cloud: cloud.Cloud{ 289 Type: "dummy", 290 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 291 }, 292 MongoInfo: mgoInfo, 293 MongoDialOpts: dialOpts, 294 } 295 st, err := state.Initialize(args) 296 c.Assert(err, jc.ErrorIsNil) 297 err = st.Close() 298 c.Check(err, jc.ErrorIsNil) 299 300 st, err = state.Initialize(args) 301 c.Check(err, gc.ErrorMatches, "already initialized") 302 if !c.Check(st, gc.IsNil) { 303 err = st.Close() 304 c.Check(err, jc.ErrorIsNil) 305 } 306 } 307 308 func (s *InitializeSuite) TestModelConfigWithAdminSecret(c *gc.C) { 309 update := map[string]interface{}{"admin-secret": "foo"} 310 remove := []string{} 311 s.testBadModelConfig(c, update, remove, "admin-secret should never be written to the state") 312 } 313 314 func (s *InitializeSuite) TestModelConfigWithCAPrivateKey(c *gc.C) { 315 update := map[string]interface{}{"ca-private-key": "foo"} 316 remove := []string{} 317 s.testBadModelConfig(c, update, remove, "ca-private-key should never be written to the state") 318 } 319 320 func (s *InitializeSuite) TestModelConfigWithoutAgentVersion(c *gc.C) { 321 update := map[string]interface{}{} 322 remove := []string{"agent-version"} 323 s.testBadModelConfig(c, update, remove, "agent-version must always be set in state") 324 } 325 326 func (s *InitializeSuite) testBadModelConfig(c *gc.C, update map[string]interface{}, remove []string, expect string) { 327 good := testing.CustomModelConfig(c, testing.Attrs{"uuid": testing.ModelTag.Id()}) 328 bad, err := good.Apply(update) 329 c.Assert(err, jc.ErrorIsNil) 330 bad, err = bad.Remove(remove) 331 c.Assert(err, jc.ErrorIsNil) 332 333 owner := names.NewLocalUserTag("initialize-admin") 334 controllerCfg := testing.FakeControllerConfig() 335 336 args := state.InitializeParams{ 337 Clock: clock.WallClock, 338 ControllerConfig: controllerCfg, 339 ControllerModelArgs: state.ModelArgs{ 340 CloudName: "dummy", 341 CloudRegion: "dummy-region", 342 Owner: owner, 343 Config: bad, 344 StorageProviderRegistry: storage.StaticProviderRegistry{}, 345 }, 346 CloudName: "dummy", 347 Cloud: cloud.Cloud{ 348 Type: "dummy", 349 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 350 Regions: []cloud.Region{{Name: "dummy-region"}}, 351 }, 352 MongoInfo: statetesting.NewMongoInfo(), 353 MongoDialOpts: mongotest.DialOpts(), 354 } 355 _, err = state.Initialize(args) 356 c.Assert(err, gc.ErrorMatches, expect) 357 358 args.ControllerModelArgs.Config = good 359 st, err := state.Initialize(args) 360 c.Assert(err, jc.ErrorIsNil) 361 st.Close() 362 363 s.openState(c, st.ModelTag()) 364 err = s.State.UpdateModelConfig(update, remove, nil) 365 c.Assert(err, gc.ErrorMatches, expect) 366 367 // ModelConfig remains inviolate. 368 cfg, err := s.State.ModelConfig() 369 c.Assert(err, jc.ErrorIsNil) 370 goodWithDefaults, err := config.New(config.UseDefaults, good.AllAttrs()) 371 c.Assert(err, jc.ErrorIsNil) 372 c.Assert(cfg.AllAttrs(), jc.DeepEquals, goodWithDefaults.AllAttrs()) 373 } 374 375 func (s *InitializeSuite) TestCloudConfigWithForbiddenValues(c *gc.C) { 376 badAttrNames := []string{ 377 "admin-secret", 378 "ca-private-key", 379 config.AgentVersionKey, 380 } 381 for _, attr := range controller.ControllerOnlyConfigAttributes { 382 badAttrNames = append(badAttrNames, attr) 383 } 384 385 modelCfg := testing.ModelConfig(c) 386 controllerCfg := testing.FakeControllerConfig() 387 args := state.InitializeParams{ 388 Clock: clock.WallClock, 389 ControllerConfig: controllerCfg, 390 ControllerModelArgs: state.ModelArgs{ 391 CloudName: "dummy", 392 Owner: names.NewLocalUserTag("initialize-admin"), 393 Config: modelCfg, 394 StorageProviderRegistry: storage.StaticProviderRegistry{}, 395 }, 396 CloudName: "dummy", 397 Cloud: cloud.Cloud{ 398 Type: "dummy", 399 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 400 }, 401 MongoInfo: statetesting.NewMongoInfo(), 402 MongoDialOpts: mongotest.DialOpts(), 403 } 404 405 for _, badAttrName := range badAttrNames { 406 badAttrs := map[string]interface{}{badAttrName: "foo"} 407 args.ControllerInheritedConfig = badAttrs 408 _, err := state.Initialize(args) 409 c.Assert(err, gc.ErrorMatches, "local cloud config cannot contain .*") 410 } 411 } 412 413 func (s *InitializeSuite) TestInitializeWithCloudRegionConfig(c *gc.C) { 414 cfg := testing.ModelConfig(c) 415 uuid := cfg.UUID() 416 417 // Phony region-config 418 regionInheritedConfigIn := cloud.RegionConfig{ 419 "a-region": cloud.Attrs{ 420 "a-key": "a-value", 421 }, 422 "b-region": cloud.Attrs{ 423 "b-key": "b-value", 424 }, 425 } 426 owner := names.NewLocalUserTag("initialize-admin") 427 controllerCfg := testing.FakeControllerConfig() 428 429 st, err := state.Initialize(state.InitializeParams{ 430 Clock: clock.WallClock, 431 ControllerConfig: controllerCfg, 432 ControllerModelArgs: state.ModelArgs{ 433 CloudName: "dummy", 434 Owner: owner, 435 Config: cfg, 436 StorageProviderRegistry: storage.StaticProviderRegistry{}, 437 }, 438 CloudName: "dummy", 439 Cloud: cloud.Cloud{ 440 Type: "dummy", 441 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 442 RegionConfig: regionInheritedConfigIn, // Init with phony region-config 443 }, 444 MongoInfo: statetesting.NewMongoInfo(), 445 MongoDialOpts: mongotest.DialOpts(), 446 }) 447 c.Assert(err, jc.ErrorIsNil) 448 c.Assert(st, gc.NotNil) 449 modelTag := st.ModelTag() 450 c.Assert(modelTag.Id(), gc.Equals, uuid) 451 err = st.Close() 452 c.Assert(err, jc.ErrorIsNil) 453 454 s.openState(c, modelTag) 455 456 for k := range regionInheritedConfigIn { 457 // Query for config for each region 458 regionInheritedConfig, err := state.ReadSettings( 459 s.State, state.GlobalSettingsC, 460 "dummy#"+k) 461 c.Assert(err, jc.ErrorIsNil) 462 c.Assert( 463 cloud.Attrs(regionInheritedConfig.Map()), 464 jc.DeepEquals, 465 regionInheritedConfigIn[k]) 466 } 467 } 468 469 func (s *InitializeSuite) TestInitializeWithCloudRegionMisses(c *gc.C) { 470 cfg := testing.ModelConfig(c) 471 uuid := cfg.UUID() 472 controllerInheritedConfigIn := map[string]interface{}{ 473 "no-proxy": "local", 474 } 475 // Phony region-config 476 regionInheritedConfigIn := cloud.RegionConfig{ 477 "a-region": cloud.Attrs{ 478 "no-proxy": "a-value", 479 }, 480 "b-region": cloud.Attrs{ 481 "no-proxy": "b-value", 482 }, 483 } 484 owner := names.NewLocalUserTag("initialize-admin") 485 controllerCfg := testing.FakeControllerConfig() 486 487 st, err := state.Initialize(state.InitializeParams{ 488 Clock: clock.WallClock, 489 ControllerConfig: controllerCfg, 490 ControllerModelArgs: state.ModelArgs{ 491 CloudName: "dummy", 492 Owner: owner, 493 Config: cfg, 494 StorageProviderRegistry: storage.StaticProviderRegistry{}, 495 }, 496 CloudName: "dummy", 497 Cloud: cloud.Cloud{ 498 Type: "dummy", 499 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 500 RegionConfig: regionInheritedConfigIn, // Init with phony region-config 501 }, 502 ControllerInheritedConfig: controllerInheritedConfigIn, 503 MongoInfo: statetesting.NewMongoInfo(), 504 MongoDialOpts: mongotest.DialOpts(), 505 }) 506 c.Assert(err, jc.ErrorIsNil) 507 c.Assert(st, gc.NotNil) 508 modelTag := st.ModelTag() 509 c.Assert(modelTag.Id(), gc.Equals, uuid) 510 err = st.Close() 511 c.Assert(err, jc.ErrorIsNil) 512 513 s.openState(c, modelTag) 514 515 var attrs map[string]interface{} 516 rspec := &environs.RegionSpec{Cloud: "dummy", Region: "c-region"} 517 got, err := s.State.ComposeNewModelConfig(attrs, rspec) 518 c.Check(err, jc.ErrorIsNil) 519 c.Assert(got["no-proxy"], gc.Equals, "local") 520 } 521 522 func (s *InitializeSuite) TestInitializeWithCloudRegionHits(c *gc.C) { 523 cfg := testing.ModelConfig(c) 524 uuid := cfg.UUID() 525 526 controllerInheritedConfigIn := map[string]interface{}{ 527 "no-proxy": "local", 528 } 529 // Phony region-config 530 regionInheritedConfigIn := cloud.RegionConfig{ 531 "a-region": cloud.Attrs{ 532 "no-proxy": "a-value", 533 }, 534 "b-region": cloud.Attrs{ 535 "no-proxy": "b-value", 536 }, 537 } 538 owner := names.NewLocalUserTag("initialize-admin") 539 controllerCfg := testing.FakeControllerConfig() 540 541 st, err := state.Initialize(state.InitializeParams{ 542 Clock: clock.WallClock, 543 ControllerConfig: controllerCfg, 544 ControllerModelArgs: state.ModelArgs{ 545 CloudName: "dummy", 546 Owner: owner, 547 Config: cfg, 548 StorageProviderRegistry: storage.StaticProviderRegistry{}, 549 }, 550 CloudName: "dummy", 551 Cloud: cloud.Cloud{ 552 Type: "dummy", 553 AuthTypes: []cloud.AuthType{cloud.EmptyAuthType}, 554 RegionConfig: regionInheritedConfigIn, // Init with phony region-config 555 }, 556 ControllerInheritedConfig: controllerInheritedConfigIn, 557 MongoInfo: statetesting.NewMongoInfo(), 558 MongoDialOpts: mongotest.DialOpts(), 559 }) 560 c.Assert(err, jc.ErrorIsNil) 561 c.Assert(st, gc.NotNil) 562 modelTag := st.ModelTag() 563 c.Assert(modelTag.Id(), gc.Equals, uuid) 564 err = st.Close() 565 c.Assert(err, jc.ErrorIsNil) 566 567 s.openState(c, modelTag) 568 569 var attrs map[string]interface{} 570 for r := range regionInheritedConfigIn { 571 rspec := &environs.RegionSpec{Cloud: "dummy", Region: r} 572 got, err := s.State.ComposeNewModelConfig(attrs, rspec) 573 c.Check(err, jc.ErrorIsNil) 574 c.Assert(got["no-proxy"], gc.Equals, regionInheritedConfigIn[r]["no-proxy"]) 575 } 576 }