github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/provider/openstack/local_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package openstack_test 5 6 import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "net/url" 13 "strings" 14 15 jc "github.com/juju/testing/checkers" 16 gc "launchpad.net/gocheck" 17 "launchpad.net/goose/client" 18 "launchpad.net/goose/identity" 19 "launchpad.net/goose/nova" 20 "launchpad.net/goose/testservices/hook" 21 "launchpad.net/goose/testservices/openstackservice" 22 23 "github.com/juju/juju/constraints" 24 "github.com/juju/juju/environs" 25 "github.com/juju/juju/environs/bootstrap" 26 "github.com/juju/juju/environs/config" 27 "github.com/juju/juju/environs/configstore" 28 "github.com/juju/juju/environs/imagemetadata" 29 "github.com/juju/juju/environs/jujutest" 30 "github.com/juju/juju/environs/simplestreams" 31 "github.com/juju/juju/environs/storage" 32 envtesting "github.com/juju/juju/environs/testing" 33 "github.com/juju/juju/environs/tools" 34 "github.com/juju/juju/instance" 35 "github.com/juju/juju/juju/arch" 36 "github.com/juju/juju/juju/testing" 37 "github.com/juju/juju/provider/common" 38 "github.com/juju/juju/provider/openstack" 39 coretesting "github.com/juju/juju/testing" 40 "github.com/juju/juju/version" 41 ) 42 43 type ProviderSuite struct { 44 restoreTimeouts func() 45 } 46 47 var _ = gc.Suite(&ProviderSuite{}) 48 var _ = gc.Suite(&localHTTPSServerSuite{}) 49 50 func (s *ProviderSuite) SetUpTest(c *gc.C) { 51 s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) 52 } 53 54 func (s *ProviderSuite) TearDownTest(c *gc.C) { 55 s.restoreTimeouts() 56 } 57 58 // Register tests to run against a test Openstack instance (service doubles). 59 func registerLocalTests() { 60 cred := &identity.Credentials{ 61 User: "fred", 62 Secrets: "secret", 63 Region: "some-region", 64 TenantName: "some tenant", 65 } 66 config := makeTestConfig(cred) 67 config["agent-version"] = version.Current.Number.String() 68 config["authorized-keys"] = "fakekey" 69 gc.Suite(&localLiveSuite{ 70 LiveTests: LiveTests{ 71 cred: cred, 72 LiveTests: jujutest.LiveTests{ 73 TestConfig: config, 74 }, 75 }, 76 }) 77 gc.Suite(&localServerSuite{ 78 cred: cred, 79 Tests: jujutest.Tests{ 80 TestConfig: config, 81 }, 82 }) 83 } 84 85 // localServer is used to spin up a local Openstack service double. 86 type localServer struct { 87 Server *httptest.Server 88 Mux *http.ServeMux 89 oldHandler http.Handler 90 Service *openstackservice.Openstack 91 restoreTimeouts func() 92 UseTLS bool 93 } 94 95 func (s *localServer) start(c *gc.C, cred *identity.Credentials) { 96 // Set up the HTTP server. 97 if s.UseTLS { 98 s.Server = httptest.NewTLSServer(nil) 99 } else { 100 s.Server = httptest.NewServer(nil) 101 } 102 c.Assert(s.Server, gc.NotNil) 103 s.oldHandler = s.Server.Config.Handler 104 s.Mux = http.NewServeMux() 105 s.Server.Config.Handler = s.Mux 106 cred.URL = s.Server.URL 107 c.Logf("Started service at: %v", s.Server.URL) 108 s.Service = openstackservice.New(cred, identity.AuthUserPass) 109 s.Service.SetupHTTP(s.Mux) 110 s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) 111 s.Service.Nova.SetAvailabilityZones( 112 nova.AvailabilityZone{Name: "test-unavailable"}, 113 nova.AvailabilityZone{ 114 Name: "test-available", 115 State: nova.AvailabilityZoneState{ 116 Available: true, 117 }, 118 }, 119 ) 120 } 121 122 func (s *localServer) stop() { 123 s.Mux = nil 124 s.Server.Config.Handler = s.oldHandler 125 s.Server.Close() 126 s.restoreTimeouts() 127 } 128 129 // localLiveSuite runs tests from LiveTests using an Openstack service double. 130 type localLiveSuite struct { 131 coretesting.BaseSuite 132 LiveTests 133 srv localServer 134 } 135 136 func (s *localLiveSuite) SetUpSuite(c *gc.C) { 137 s.BaseSuite.SetUpSuite(c) 138 c.Logf("Running live tests using openstack service test double") 139 s.srv.start(c, s.cred) 140 s.LiveTests.SetUpSuite(c) 141 openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred) 142 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 143 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 144 } 145 146 func (s *localLiveSuite) TearDownSuite(c *gc.C) { 147 openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env)) 148 s.LiveTests.TearDownSuite(c) 149 s.srv.stop() 150 s.BaseSuite.TearDownSuite(c) 151 } 152 153 func (s *localLiveSuite) SetUpTest(c *gc.C) { 154 s.BaseSuite.SetUpTest(c) 155 s.LiveTests.SetUpTest(c) 156 s.PatchValue(&imagemetadata.DefaultBaseURL, "") 157 } 158 159 func (s *localLiveSuite) TearDownTest(c *gc.C) { 160 s.LiveTests.TearDownTest(c) 161 s.BaseSuite.TearDownTest(c) 162 } 163 164 // localServerSuite contains tests that run against an Openstack service double. 165 // These tests can test things that would be unreasonably slow or expensive 166 // to test on a live Openstack server. The service double is started and stopped for 167 // each test. 168 type localServerSuite struct { 169 coretesting.BaseSuite 170 jujutest.Tests 171 cred *identity.Credentials 172 srv localServer 173 toolsMetadataStorage storage.Storage 174 imageMetadataStorage storage.Storage 175 } 176 177 func (s *localServerSuite) SetUpSuite(c *gc.C) { 178 s.BaseSuite.SetUpSuite(c) 179 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 180 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 181 c.Logf("Running local tests") 182 } 183 184 func (s *localServerSuite) SetUpTest(c *gc.C) { 185 s.BaseSuite.SetUpTest(c) 186 s.srv.start(c, s.cred) 187 cl := client.NewClient(s.cred, identity.AuthUserPass, nil) 188 err := cl.Authenticate() 189 c.Assert(err, gc.IsNil) 190 containerURL, err := cl.MakeServiceURL("object-store", nil) 191 c.Assert(err, gc.IsNil) 192 s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{ 193 "tools-metadata-url": containerURL + "/juju-dist-test/tools", 194 "image-metadata-url": containerURL + "/juju-dist-test", 195 "auth-url": s.cred.URL, 196 }) 197 s.Tests.SetUpTest(c) 198 // For testing, we create a storage instance to which is uploaded tools and image metadata. 199 env := s.Prepare(c) 200 s.toolsMetadataStorage = openstack.MetadataStorage(env) 201 // Put some fake metadata in place so that tests that are simply 202 // starting instances without any need to check if those instances 203 // are running can find the metadata. 204 envtesting.UploadFakeTools(c, s.toolsMetadataStorage) 205 s.imageMetadataStorage = openstack.ImageMetadataStorage(env) 206 openstack.UseTestImageData(s.imageMetadataStorage, s.cred) 207 } 208 209 func (s *localServerSuite) TearDownTest(c *gc.C) { 210 if s.imageMetadataStorage != nil { 211 openstack.RemoveTestImageData(s.imageMetadataStorage) 212 } 213 if s.toolsMetadataStorage != nil { 214 envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage) 215 } 216 s.Tests.TearDownTest(c) 217 s.srv.stop() 218 s.BaseSuite.TearDownTest(c) 219 } 220 221 // If the bootstrap node is configured to require a public IP address, 222 // bootstrapping fails if an address cannot be allocated. 223 func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) { 224 cleanup := s.srv.Service.Nova.RegisterControlPoint( 225 "addFloatingIP", 226 func(sc hook.ServiceControl, args ...interface{}) error { 227 return fmt.Errorf("failed on purpose") 228 }, 229 ) 230 defer cleanup() 231 232 // Create a config that matches s.TestConfig but with use-floating-ip set to true 233 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 234 "use-floating-ip": true, 235 })) 236 c.Assert(err, gc.IsNil) 237 env, err := environs.New(cfg) 238 c.Assert(err, gc.IsNil) 239 err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 240 c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*") 241 } 242 243 // If the environment is configured not to require a public IP address for nodes, 244 // bootstrapping and starting an instance should occur without any attempt to 245 // allocate a public address. 246 func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) { 247 cleanup := s.srv.Service.Nova.RegisterControlPoint( 248 "addFloatingIP", 249 func(sc hook.ServiceControl, args ...interface{}) error { 250 return fmt.Errorf("add floating IP should not have been called") 251 }, 252 ) 253 defer cleanup() 254 cleanup = s.srv.Service.Nova.RegisterControlPoint( 255 "addServerFloatingIP", 256 func(sc hook.ServiceControl, args ...interface{}) error { 257 return fmt.Errorf("add server floating IP should not have been called") 258 }, 259 ) 260 defer cleanup() 261 262 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 263 "use-floating-ip": false, 264 })) 265 c.Assert(err, gc.IsNil) 266 env, err := environs.Prepare(cfg, coretesting.Context(c), s.ConfigStore) 267 c.Assert(err, gc.IsNil) 268 err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 269 c.Assert(err, gc.IsNil) 270 inst, _ := testing.AssertStartInstance(c, env, "100") 271 err = env.StopInstances(inst.Id()) 272 c.Assert(err, gc.IsNil) 273 } 274 275 func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { 276 // Ensure amd64 tools are available, to ensure an amd64 image. 277 amd64Version := version.Current 278 amd64Version.Arch = arch.AMD64 279 for _, series := range bootstrap.ToolsLtsSeries { 280 amd64Version.Series = series 281 envtesting.AssertUploadFakeToolsVersions(c, s.toolsMetadataStorage, amd64Version) 282 } 283 284 env := s.Prepare(c) 285 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 286 c.Assert(err, gc.IsNil) 287 _, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024")) 288 c.Check(*hc.Arch, gc.Equals, "amd64") 289 c.Check(*hc.Mem, gc.Equals, uint64(2048)) 290 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 291 c.Assert(hc.CpuPower, gc.IsNil) 292 } 293 294 func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) { 295 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 296 // A label that corresponds to a nova test service network 297 "network": "net", 298 })) 299 c.Assert(err, gc.IsNil) 300 env, err := environs.New(cfg) 301 c.Assert(err, gc.IsNil) 302 inst, _ := testing.AssertStartInstance(c, env, "100") 303 err = env.StopInstances(inst.Id()) 304 c.Assert(err, gc.IsNil) 305 } 306 307 func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) { 308 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 309 // A label that has no related network in the nova test service 310 "network": "no-network-with-this-label", 311 })) 312 c.Assert(err, gc.IsNil) 313 env, err := environs.New(cfg) 314 c.Assert(err, gc.IsNil) 315 inst, _, _, err := testing.StartInstance(env, "100") 316 c.Check(inst, gc.IsNil) 317 c.Assert(err, gc.ErrorMatches, "No networks exist with label .*") 318 } 319 320 func (s *localServerSuite) TestStartInstanceNetworkUnknownId(c *gc.C) { 321 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 322 // A valid UUID but no related network in the nova test service 323 "network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", 324 })) 325 c.Assert(err, gc.IsNil) 326 env, err := environs.New(cfg) 327 c.Assert(err, gc.IsNil) 328 inst, _, _, err := testing.StartInstance(env, "100") 329 c.Check(inst, gc.IsNil) 330 c.Assert(err, gc.ErrorMatches, "cannot run instance: (\\n|.)*"+ 331 "caused by: "+ 332 "request \\(.*/servers\\) returned unexpected status: "+ 333 "404; error info: .*itemNotFound.*") 334 } 335 336 func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) { 337 novaClient := openstack.GetNovaClient(env) 338 groups, err := novaClient.ListSecurityGroups() 339 c.Assert(err, gc.IsNil) 340 for _, name := range expected { 341 found := false 342 for _, group := range groups { 343 if group.Name == name { 344 found = true 345 break 346 } 347 } 348 if !found { 349 c.Errorf("expected security group %q not found", name) 350 } 351 } 352 for _, group := range groups { 353 found := false 354 for _, name := range expected { 355 if group.Name == name { 356 found = true 357 break 358 } 359 } 360 if !found { 361 c.Errorf("existing security group %q is not expected", group.Name) 362 } 363 } 364 } 365 366 func (s *localServerSuite) TestStopInstance(c *gc.C) { 367 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 368 "firewall-mode": "instance"})) 369 c.Assert(err, gc.IsNil) 370 env, err := environs.New(cfg) 371 c.Assert(err, gc.IsNil) 372 instanceName := "100" 373 inst, _ := testing.AssertStartInstance(c, env, instanceName) 374 // Openstack now has three security groups for the server, the default 375 // group, one group for the entire environment, and another for the 376 // new instance. 377 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}) 378 err = env.StopInstances(inst.Id()) 379 c.Assert(err, gc.IsNil) 380 // The security group for this instance is now removed. 381 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name())}) 382 } 383 384 // Due to bug #1300755 it can happen that the security group intended for 385 // an instance is also used as the common security group of another 386 // environment. If this is the case, the attempt to delete the instance's 387 // security group fails but StopInstance succeeds. 388 func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) { 389 // Force an error when a security group is deleted. 390 cleanup := s.srv.Service.Nova.RegisterControlPoint( 391 "removeSecurityGroup", 392 func(sc hook.ServiceControl, args ...interface{}) error { 393 return fmt.Errorf("failed on purpose") 394 }, 395 ) 396 defer cleanup() 397 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 398 "firewall-mode": "instance"})) 399 c.Assert(err, gc.IsNil) 400 env, err := environs.New(cfg) 401 c.Assert(err, gc.IsNil) 402 instanceName := "100" 403 inst, _ := testing.AssertStartInstance(c, env, instanceName) 404 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)} 405 assertSecurityGroups(c, env, allSecurityGroups) 406 err = env.StopInstances(inst.Id()) 407 c.Assert(err, gc.IsNil) 408 assertSecurityGroups(c, env, allSecurityGroups) 409 } 410 411 func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) { 412 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 413 "firewall-mode": "instance"})) 414 c.Assert(err, gc.IsNil) 415 env, err := environs.New(cfg) 416 c.Assert(err, gc.IsNil) 417 instanceName := "100" 418 testing.AssertStartInstance(c, env, instanceName) 419 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)} 420 assertSecurityGroups(c, env, allSecurityGroups) 421 err = env.Destroy() 422 c.Check(err, gc.IsNil) 423 assertSecurityGroups(c, env, []string{"default"}) 424 } 425 426 func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) { 427 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 428 "firewall-mode": "global"})) 429 c.Assert(err, gc.IsNil) 430 env, err := environs.New(cfg) 431 c.Assert(err, gc.IsNil) 432 instanceName := "100" 433 testing.AssertStartInstance(c, env, instanceName) 434 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-global", env.Name())} 435 assertSecurityGroups(c, env, allSecurityGroups) 436 err = env.Destroy() 437 c.Check(err, gc.IsNil) 438 assertSecurityGroups(c, env, []string{"default"}) 439 } 440 441 var instanceGathering = []struct { 442 ids []instance.Id 443 err error 444 }{ 445 {ids: []instance.Id{"id0"}}, 446 {ids: []instance.Id{"id0", "id0"}}, 447 {ids: []instance.Id{"id0", "id1"}}, 448 {ids: []instance.Id{"id1", "id0"}}, 449 {ids: []instance.Id{"id1", "id0", "id1"}}, 450 { 451 ids: []instance.Id{""}, 452 err: environs.ErrNoInstances, 453 }, 454 { 455 ids: []instance.Id{"", ""}, 456 err: environs.ErrNoInstances, 457 }, 458 { 459 ids: []instance.Id{"", "", ""}, 460 err: environs.ErrNoInstances, 461 }, 462 { 463 ids: []instance.Id{"id0", ""}, 464 err: environs.ErrPartialInstances, 465 }, 466 { 467 ids: []instance.Id{"", "id1"}, 468 err: environs.ErrPartialInstances, 469 }, 470 { 471 ids: []instance.Id{"id0", "id1", ""}, 472 err: environs.ErrPartialInstances, 473 }, 474 { 475 ids: []instance.Id{"id0", "", "id0"}, 476 err: environs.ErrPartialInstances, 477 }, 478 { 479 ids: []instance.Id{"id0", "id0", ""}, 480 err: environs.ErrPartialInstances, 481 }, 482 { 483 ids: []instance.Id{"", "id0", "id1"}, 484 err: environs.ErrPartialInstances, 485 }, 486 } 487 488 func (s *localServerSuite) TestInstanceStatus(c *gc.C) { 489 env := s.Prepare(c) 490 // goose's test service always returns ACTIVE state. 491 inst, _ := testing.AssertStartInstance(c, env, "100") 492 c.Assert(inst.Status(), gc.Equals, nova.StatusActive) 493 err := env.StopInstances(inst.Id()) 494 c.Assert(err, gc.IsNil) 495 } 496 497 func (s *localServerSuite) TestInstancesGathering(c *gc.C) { 498 env := s.Prepare(c) 499 inst0, _ := testing.AssertStartInstance(c, env, "100") 500 id0 := inst0.Id() 501 inst1, _ := testing.AssertStartInstance(c, env, "101") 502 id1 := inst1.Id() 503 defer func() { 504 err := env.StopInstances(inst0.Id(), inst1.Id()) 505 c.Assert(err, gc.IsNil) 506 }() 507 508 for i, test := range instanceGathering { 509 c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err) 510 ids := make([]instance.Id, len(test.ids)) 511 for j, id := range test.ids { 512 switch id { 513 case "id0": 514 ids[j] = id0 515 case "id1": 516 ids[j] = id1 517 } 518 } 519 insts, err := env.Instances(ids) 520 c.Assert(err, gc.Equals, test.err) 521 if err == environs.ErrNoInstances { 522 c.Assert(insts, gc.HasLen, 0) 523 } else { 524 c.Assert(insts, gc.HasLen, len(test.ids)) 525 } 526 for j, inst := range insts { 527 if ids[j] != "" { 528 c.Assert(inst.Id(), gc.Equals, ids[j]) 529 } else { 530 c.Assert(inst, gc.IsNil) 531 } 532 } 533 } 534 } 535 536 func (s *localServerSuite) TestCollectInstances(c *gc.C) { 537 env := s.Prepare(c) 538 cleanup := s.srv.Service.Nova.RegisterControlPoint( 539 "addServer", 540 func(sc hook.ServiceControl, args ...interface{}) error { 541 details := args[0].(*nova.ServerDetail) 542 details.Status = "BUILD(networking)" 543 return nil 544 }, 545 ) 546 defer cleanup() 547 stateInst, _ := testing.AssertStartInstance(c, env, "100") 548 defer func() { 549 err := env.StopInstances(stateInst.Id()) 550 c.Assert(err, gc.IsNil) 551 }() 552 found := make(map[instance.Id]instance.Instance) 553 missing := []instance.Id{stateInst.Id()} 554 555 resultMissing := openstack.CollectInstances(env, missing, found) 556 557 c.Assert(resultMissing, gc.DeepEquals, missing) 558 } 559 560 func (s *localServerSuite) TestInstancesBuildSpawning(c *gc.C) { 561 env := s.Prepare(c) 562 // HP servers are available once they are BUILD(spawning). 563 cleanup := s.srv.Service.Nova.RegisterControlPoint( 564 "addServer", 565 func(sc hook.ServiceControl, args ...interface{}) error { 566 details := args[0].(*nova.ServerDetail) 567 details.Status = nova.StatusBuildSpawning 568 return nil 569 }, 570 ) 571 defer cleanup() 572 stateInst, _ := testing.AssertStartInstance(c, env, "100") 573 defer func() { 574 err := env.StopInstances(stateInst.Id()) 575 c.Assert(err, gc.IsNil) 576 }() 577 578 instances, err := env.Instances([]instance.Id{stateInst.Id()}) 579 580 c.Assert(err, gc.IsNil) 581 c.Assert(instances, gc.HasLen, 1) 582 c.Assert(instances[0].Status(), gc.Equals, nova.StatusBuildSpawning) 583 } 584 585 // TODO (wallyworld) - this test was copied from the ec2 provider. 586 // It should be moved to environs.jujutests.Tests. 587 func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { 588 env := s.Prepare(c) 589 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 590 c.Assert(err, gc.IsNil) 591 592 // check that the state holds the id of the bootstrap machine. 593 stateData, err := bootstrap.LoadState(env.Storage()) 594 c.Assert(err, gc.IsNil) 595 c.Assert(stateData.StateInstances, gc.HasLen, 1) 596 597 insts, err := env.AllInstances() 598 c.Assert(err, gc.IsNil) 599 c.Assert(insts, gc.HasLen, 1) 600 c.Check(insts[0].Id(), gc.Equals, stateData.StateInstances[0]) 601 602 addresses, err := insts[0].Addresses() 603 c.Assert(err, gc.IsNil) 604 c.Assert(addresses, gc.Not(gc.HasLen), 0) 605 606 // TODO(wallyworld) - 2013-03-01 bug=1137005 607 // The nova test double needs to be updated to support retrieving instance userData. 608 // Until then, we can't check the cloud init script was generated correctly. 609 // When we can, we should also check cloudinit for a non-manager node (as in the 610 // ec2 tests). 611 } 612 613 func (s *localServerSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { 614 // Create a config that matches s.TestConfig but with the specified stream. 615 envAttrs := s.TestConfig 616 if stream != "" { 617 envAttrs = envAttrs.Merge(coretesting.Attrs{"image-stream": stream}) 618 } 619 cfg, err := config.New(config.NoDefaults, envAttrs) 620 c.Assert(err, gc.IsNil) 621 env, err := environs.New(cfg) 622 c.Assert(err, gc.IsNil) 623 sources, err := imagemetadata.GetMetadataSources(env) 624 c.Assert(err, gc.IsNil) 625 c.Assert(sources, gc.HasLen, 4) 626 var urls = make([]string, len(sources)) 627 for i, source := range sources { 628 url, err := source.URL("") 629 c.Assert(err, gc.IsNil) 630 urls[i] = url 631 } 632 // The image-metadata-url ends with "/juju-dist-test/". 633 c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue) 634 // The control bucket URL contains the bucket name. 635 c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/images"), jc.IsTrue) 636 // The product-streams URL ends with "/imagemetadata". 637 c.Check(strings.HasSuffix(urls[2], "/imagemetadata/"), jc.IsTrue) 638 c.Assert(urls[3], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) 639 } 640 641 func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) { 642 s.assertGetImageMetadataSources(c, "", "releases") 643 s.assertGetImageMetadataSources(c, "released", "releases") 644 s.assertGetImageMetadataSources(c, "daily", "daily") 645 } 646 647 func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { 648 env := s.Open(c) 649 sources, err := tools.GetMetadataSources(env) 650 c.Assert(err, gc.IsNil) 651 c.Assert(sources, gc.HasLen, 3) 652 var urls = make([]string, len(sources)) 653 for i, source := range sources { 654 url, err := source.URL("") 655 c.Assert(err, gc.IsNil) 656 urls[i] = url 657 } 658 // The tools-metadata-url ends with "/juju-dist-test/tools/". 659 c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue) 660 // The control bucket URL contains the bucket name. 661 c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/tools"), jc.IsTrue) 662 // Check that the URL from keystone parses. 663 _, err = url.Parse(urls[2]) 664 c.Assert(err, gc.IsNil) 665 } 666 667 func (s *localServerSuite) TestSupportedArchitectures(c *gc.C) { 668 env := s.Open(c) 669 a, err := env.SupportedArchitectures() 670 c.Assert(err, gc.IsNil) 671 c.Assert(a, jc.SameContents, []string{"amd64", "i386", "ppc64"}) 672 } 673 674 func (s *localServerSuite) TestSupportNetworks(c *gc.C) { 675 env := s.Open(c) 676 c.Assert(env.SupportNetworks(), jc.IsFalse) 677 } 678 679 func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) { 680 // Prevent falling over to the public datasource. 681 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 682 683 env := s.Open(c) 684 685 // An error occurs if no suitable image is found. 686 _, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G") 687 c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`) 688 } 689 690 func (s *localServerSuite) TestConstraintsValidator(c *gc.C) { 691 env := s.Open(c) 692 validator, err := env.ConstraintsValidator() 693 c.Assert(err, gc.IsNil) 694 cons := constraints.MustParse("arch=amd64 cpu-power=10") 695 unsupported, err := validator.Validate(cons) 696 c.Assert(err, gc.IsNil) 697 c.Assert(unsupported, jc.SameContents, []string{"cpu-power"}) 698 } 699 700 func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) { 701 env := s.Open(c) 702 validator, err := env.ConstraintsValidator() 703 c.Assert(err, gc.IsNil) 704 cons := constraints.MustParse("arch=arm64") 705 _, err = validator.Validate(cons) 706 c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=arm64\nvalid values are:.*") 707 cons = constraints.MustParse("instance-type=foo") 708 _, err = validator.Validate(cons) 709 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*") 710 } 711 712 func (s *localServerSuite) TestConstraintsMerge(c *gc.C) { 713 env := s.Open(c) 714 validator, err := env.ConstraintsValidator() 715 c.Assert(err, gc.IsNil) 716 consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G") 717 consB := constraints.MustParse("instance-type=m1.small") 718 cons, err := validator.Merge(consA, consB) 719 c.Assert(err, gc.IsNil) 720 c.Assert(cons, gc.DeepEquals, constraints.MustParse("instance-type=m1.small")) 721 } 722 723 func (s *localServerSuite) TestFindImageInstanceConstraint(c *gc.C) { 724 // Prevent falling over to the public datasource. 725 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 726 727 env := s.Open(c) 728 spec, err := openstack.FindInstanceSpec(env, "precise", "amd64", "instance-type=m1.tiny") 729 c.Assert(err, gc.IsNil) 730 c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny") 731 } 732 733 func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) { 734 // Prevent falling over to the public datasource. 735 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 736 737 env := s.Open(c) 738 _, err := openstack.FindInstanceSpec(env, "precise", "amd64", "instance-type=m1.large") 739 c.Assert(err, gc.ErrorMatches, `invalid instance type "m1.large"`) 740 } 741 742 func (s *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) { 743 env := s.Open(c) 744 cons := constraints.MustParse("instance-type=m1.small") 745 placement := "" 746 err := env.PrecheckInstance("precise", cons, placement) 747 c.Assert(err, gc.IsNil) 748 } 749 750 func (s *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) { 751 env := s.Open(c) 752 cons := constraints.MustParse("instance-type=m1.large") 753 placement := "" 754 err := env.PrecheckInstance("precise", cons, placement) 755 c.Assert(err, gc.ErrorMatches, `invalid Openstack flavour "m1.large" specified`) 756 } 757 758 func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) { 759 env := t.Prepare(c) 760 placement := "zone=test-available" 761 err := env.PrecheckInstance("precise", constraints.Value{}, placement) 762 c.Assert(err, gc.IsNil) 763 } 764 765 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) { 766 env := t.Prepare(c) 767 placement := "zone=test-unavailable" 768 err := env.PrecheckInstance("precise", constraints.Value{}, placement) 769 c.Assert(err, gc.IsNil) 770 } 771 772 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) { 773 env := t.Prepare(c) 774 placement := "zone=test-unknown" 775 err := env.PrecheckInstance("precise", constraints.Value{}, placement) 776 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 777 } 778 779 func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) { 780 env := s.Open(c) 781 params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region") 782 c.Assert(err, gc.IsNil) 783 params.Sources, err = imagemetadata.GetMetadataSources(env) 784 c.Assert(err, gc.IsNil) 785 params.Series = "raring" 786 image_ids, _, err := imagemetadata.ValidateImageMetadata(params) 787 c.Assert(err, gc.IsNil) 788 c.Assert(image_ids, jc.SameContents, []string{"id-y"}) 789 } 790 791 func (s *localServerSuite) TestRemoveAll(c *gc.C) { 792 env := s.Prepare(c) 793 stor := env.Storage() 794 for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") { 795 content := []byte{a} 796 name := string(content) 797 err := stor.Put(name, bytes.NewBuffer(content), 798 int64(len(content))) 799 c.Assert(err, gc.IsNil) 800 } 801 reader, err := storage.Get(stor, "a") 802 c.Assert(err, gc.IsNil) 803 allContent, err := ioutil.ReadAll(reader) 804 c.Assert(err, gc.IsNil) 805 c.Assert(string(allContent), gc.Equals, "a") 806 err = stor.RemoveAll() 807 c.Assert(err, gc.IsNil) 808 _, err = storage.Get(stor, "a") 809 c.Assert(err, gc.NotNil) 810 } 811 812 func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) { 813 env := s.Prepare(c) 814 stor := env.Storage() 815 // 6*26 = 156 items 816 for _, a := range []byte("abcdef") { 817 for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") { 818 content := []byte{a, b} 819 name := string(content) 820 err := stor.Put(name, bytes.NewBuffer(content), 821 int64(len(content))) 822 c.Assert(err, gc.IsNil) 823 } 824 } 825 reader, err := storage.Get(stor, "ab") 826 c.Assert(err, gc.IsNil) 827 allContent, err := ioutil.ReadAll(reader) 828 c.Assert(err, gc.IsNil) 829 c.Assert(string(allContent), gc.Equals, "ab") 830 err = stor.RemoveAll() 831 c.Assert(err, gc.IsNil) 832 _, err = storage.Get(stor, "ab") 833 c.Assert(err, gc.NotNil) 834 } 835 836 // TestEnsureGroup checks that when creating a duplicate security group, the existing group is 837 // returned and the existing rules have been left as is. 838 func (s *localServerSuite) TestEnsureGroup(c *gc.C) { 839 env := s.Prepare(c) 840 rule := []nova.RuleInfo{ 841 { 842 IPProtocol: "tcp", 843 FromPort: 22, 844 ToPort: 22, 845 }, 846 } 847 848 assertRule := func(group nova.SecurityGroup) { 849 c.Check(len(group.Rules), gc.Equals, 1) 850 c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp") 851 c.Check(*group.Rules[0].FromPort, gc.Equals, 22) 852 c.Check(*group.Rules[0].ToPort, gc.Equals, 22) 853 } 854 855 group, err := openstack.EnsureGroup(env, "test group", rule) 856 c.Assert(err, gc.IsNil) 857 c.Assert(group.Name, gc.Equals, "test group") 858 assertRule(group) 859 id := group.Id 860 // Do it again and check that the existing group is returned. 861 anotherRule := []nova.RuleInfo{ 862 { 863 IPProtocol: "tcp", 864 FromPort: 1, 865 ToPort: 65535, 866 }, 867 } 868 group, err = openstack.EnsureGroup(env, "test group", anotherRule) 869 c.Assert(err, gc.IsNil) 870 c.Check(group.Id, gc.Equals, id) 871 c.Assert(group.Name, gc.Equals, "test group") 872 assertRule(group) 873 } 874 875 // localHTTPSServerSuite contains tests that run against an Openstack service 876 // double connected on an HTTPS port with a self-signed certificate. This 877 // service is set up and torn down for every test. This should only test 878 // things that depend on the HTTPS connection, all other functional tests on a 879 // local connection should be in localServerSuite 880 type localHTTPSServerSuite struct { 881 coretesting.BaseSuite 882 attrs map[string]interface{} 883 cred *identity.Credentials 884 srv localServer 885 env environs.Environ 886 } 887 888 func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} { 889 attrs := makeTestConfig(s.cred) 890 attrs["agent-version"] = version.Current.Number.String() 891 attrs["authorized-keys"] = "fakekey" 892 // In order to set up and tear down the environment properly, we must 893 // disable hostname verification 894 attrs["ssl-hostname-verification"] = false 895 attrs["auth-url"] = s.cred.URL 896 // Now connect and set up test-local tools and image-metadata URLs 897 cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil) 898 err := cl.Authenticate() 899 c.Assert(err, gc.IsNil) 900 containerURL, err := cl.MakeServiceURL("object-store", nil) 901 c.Assert(err, gc.IsNil) 902 c.Check(containerURL[:8], gc.Equals, "https://") 903 attrs["tools-metadata-url"] = containerURL + "/juju-dist-test/tools" 904 c.Logf("Set tools-metadata-url=%q", attrs["tools-metadata-url"]) 905 attrs["image-metadata-url"] = containerURL + "/juju-dist-test" 906 c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"]) 907 return attrs 908 } 909 910 func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) { 911 s.BaseSuite.SetUpTest(c) 912 s.srv.UseTLS = true 913 cred := &identity.Credentials{ 914 User: "fred", 915 Secrets: "secret", 916 Region: "some-region", 917 TenantName: "some tenant", 918 } 919 // Note: start() will change cred.URL to point to s.srv.Server.URL 920 s.srv.start(c, cred) 921 s.cred = cred 922 attrs := s.createConfigAttrs(c) 923 c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://") 924 cfg, err := config.New(config.NoDefaults, attrs) 925 c.Assert(err, gc.IsNil) 926 s.env, err = environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem()) 927 c.Assert(err, gc.IsNil) 928 s.attrs = s.env.Config().AllAttrs() 929 } 930 931 func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) { 932 if s.env != nil { 933 err := s.env.Destroy() 934 c.Check(err, gc.IsNil) 935 s.env = nil 936 } 937 s.srv.stop() 938 s.BaseSuite.TearDownTest(c) 939 } 940 941 func (s *localHTTPSServerSuite) TestCanUploadTools(c *gc.C) { 942 envtesting.UploadFakeTools(c, s.env.Storage()) 943 } 944 945 func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) { 946 // If you don't have ssl-hostname-verification set to false, then we 947 // fail to connect to the environment. Copy the attrs used by SetUp and 948 // force hostname verification. 949 newattrs := make(map[string]interface{}, len(s.attrs)) 950 for k, v := range s.attrs { 951 newattrs[k] = v 952 } 953 newattrs["ssl-hostname-verification"] = true 954 env, err := environs.NewFromAttrs(newattrs) 955 c.Assert(err, gc.IsNil) 956 err = env.Storage().Put("test-name", strings.NewReader("content"), 7) 957 c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") 958 // However, it works just fine if you use the one with the credentials set 959 err = s.env.Storage().Put("test-name", strings.NewReader("content"), 7) 960 c.Assert(err, gc.IsNil) 961 _, err = env.Storage().Get("test-name") 962 c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") 963 reader, err := s.env.Storage().Get("test-name") 964 c.Assert(err, gc.IsNil) 965 contents, err := ioutil.ReadAll(reader) 966 c.Assert(string(contents), gc.Equals, "content") 967 } 968 969 func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) { 970 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 971 defer restoreFinishBootstrap() 972 973 // For testing, we create a storage instance to which is uploaded tools and image metadata. 974 metadataStorage := openstack.MetadataStorage(s.env) 975 url, err := metadataStorage.URL("") 976 c.Assert(err, gc.IsNil) 977 c.Logf("Generating fake tools for: %v", url) 978 envtesting.UploadFakeTools(c, metadataStorage) 979 defer envtesting.RemoveFakeTools(c, metadataStorage) 980 openstack.UseTestImageData(metadataStorage, s.cred) 981 defer openstack.RemoveTestImageData(metadataStorage) 982 983 err = bootstrap.Bootstrap(coretesting.Context(c), s.env, environs.BootstrapParams{}) 984 c.Assert(err, gc.IsNil) 985 } 986 987 func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) { 988 // Setup a custom URL for image metadata 989 customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata") 990 customURL, err := customStorage.URL("") 991 c.Assert(err, gc.IsNil) 992 c.Check(customURL[:8], gc.Equals, "https://") 993 994 config, err := s.env.Config().Apply( 995 map[string]interface{}{"image-metadata-url": customURL}, 996 ) 997 c.Assert(err, gc.IsNil) 998 err = s.env.SetConfig(config) 999 c.Assert(err, gc.IsNil) 1000 sources, err := imagemetadata.GetMetadataSources(s.env) 1001 c.Assert(err, gc.IsNil) 1002 c.Assert(sources, gc.HasLen, 4) 1003 1004 // Make sure there is something to download from each location 1005 private := "private-content" 1006 err = s.env.Storage().Put("images/"+private, bytes.NewBufferString(private), int64(len(private))) 1007 c.Assert(err, gc.IsNil) 1008 1009 metadata := "metadata-content" 1010 metadataStorage := openstack.ImageMetadataStorage(s.env) 1011 err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata))) 1012 c.Assert(err, gc.IsNil) 1013 1014 custom := "custom-content" 1015 err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) 1016 c.Assert(err, gc.IsNil) 1017 1018 // Read from the Config entry's image-metadata-url 1019 contentReader, url, err := sources[0].Fetch(custom) 1020 c.Assert(err, gc.IsNil) 1021 defer contentReader.Close() 1022 content, err := ioutil.ReadAll(contentReader) 1023 c.Assert(err, gc.IsNil) 1024 c.Assert(string(content), gc.Equals, custom) 1025 c.Check(url[:8], gc.Equals, "https://") 1026 1027 // Read from the private bucket 1028 contentReader, url, err = sources[1].Fetch(private) 1029 c.Assert(err, gc.IsNil) 1030 defer contentReader.Close() 1031 content, err = ioutil.ReadAll(contentReader) 1032 c.Assert(err, gc.IsNil) 1033 c.Check(string(content), gc.Equals, private) 1034 c.Check(url[:8], gc.Equals, "https://") 1035 1036 // Check the entry we got from keystone 1037 contentReader, url, err = sources[2].Fetch(metadata) 1038 c.Assert(err, gc.IsNil) 1039 defer contentReader.Close() 1040 content, err = ioutil.ReadAll(contentReader) 1041 c.Assert(err, gc.IsNil) 1042 c.Assert(string(content), gc.Equals, metadata) 1043 c.Check(url[:8], gc.Equals, "https://") 1044 // Verify that we are pointing at exactly where metadataStorage thinks we are 1045 metaURL, err := metadataStorage.URL(metadata) 1046 c.Assert(err, gc.IsNil) 1047 c.Check(url, gc.Equals, metaURL) 1048 1049 } 1050 1051 func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) { 1052 // Setup a custom URL for image metadata 1053 customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata") 1054 customURL, err := customStorage.URL("") 1055 c.Assert(err, gc.IsNil) 1056 c.Check(customURL[:8], gc.Equals, "https://") 1057 1058 config, err := s.env.Config().Apply( 1059 map[string]interface{}{"tools-metadata-url": customURL}, 1060 ) 1061 c.Assert(err, gc.IsNil) 1062 err = s.env.SetConfig(config) 1063 c.Assert(err, gc.IsNil) 1064 sources, err := tools.GetMetadataSources(s.env) 1065 c.Assert(err, gc.IsNil) 1066 c.Assert(sources, gc.HasLen, 4) 1067 1068 // Make sure there is something to download from each location 1069 private := "private-tools-content" 1070 // The Private data storage always tacks on "tools/" to the URL stream, 1071 // so add it in here 1072 err = s.env.Storage().Put("tools/"+private, bytes.NewBufferString(private), int64(len(private))) 1073 c.Assert(err, gc.IsNil) 1074 1075 keystone := "keystone-tools-content" 1076 // The keystone entry just points at the root of the Swift storage, and 1077 // we have to create a container to upload any data. So we just point 1078 // into a subdirectory for the data we are downloading 1079 keystoneContainer := "tools-test" 1080 keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test") 1081 err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone))) 1082 c.Assert(err, gc.IsNil) 1083 1084 custom := "custom-tools-content" 1085 err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) 1086 c.Assert(err, gc.IsNil) 1087 1088 // Read from the Config entry's tools-metadata-url 1089 contentReader, url, err := sources[0].Fetch(custom) 1090 c.Assert(err, gc.IsNil) 1091 defer contentReader.Close() 1092 content, err := ioutil.ReadAll(contentReader) 1093 c.Assert(err, gc.IsNil) 1094 c.Assert(string(content), gc.Equals, custom) 1095 c.Check(url[:8], gc.Equals, "https://") 1096 1097 // Read from the private bucket 1098 contentReader, url, err = sources[1].Fetch(private) 1099 c.Assert(err, gc.IsNil) 1100 defer contentReader.Close() 1101 content, err = ioutil.ReadAll(contentReader) 1102 c.Assert(err, gc.IsNil) 1103 c.Check(string(content), gc.Equals, private) 1104 //c.Check(url[:8], gc.Equals, "https://") 1105 c.Check(strings.HasSuffix(url, "tools/"+private), jc.IsTrue) 1106 1107 // Check the entry we got from keystone 1108 // Now fetch the data, and verify the contents. 1109 contentReader, url, err = sources[2].Fetch(keystoneContainer + "/" + keystone) 1110 c.Assert(err, gc.IsNil) 1111 defer contentReader.Close() 1112 content, err = ioutil.ReadAll(contentReader) 1113 c.Assert(err, gc.IsNil) 1114 c.Assert(string(content), gc.Equals, keystone) 1115 c.Check(url[:8], gc.Equals, "https://") 1116 keystoneURL, err := keystoneStorage.URL(keystone) 1117 c.Assert(err, gc.IsNil) 1118 c.Check(url, gc.Equals, keystoneURL) 1119 1120 // We *don't* test Fetch for sources[3] because it points to 1121 // streams.canonical.com 1122 } 1123 1124 func (s *localServerSuite) TestAllInstancesIgnoresOtherMachines(c *gc.C) { 1125 env := s.Prepare(c) 1126 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 1127 c.Assert(err, gc.IsNil) 1128 1129 // Check that we see 1 instance in the environment 1130 insts, err := env.AllInstances() 1131 c.Assert(err, gc.IsNil) 1132 c.Check(insts, gc.HasLen, 1) 1133 1134 // Now start a machine 'manually' in the same account, with a similar 1135 // but not matching name, and ensure it isn't seen by AllInstances 1136 // See bug #1257481, for how similar names were causing them to get 1137 // listed (and thus destroyed) at the wrong time 1138 existingEnvName := s.TestConfig["name"] 1139 newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingEnvName) 1140 1141 // We grab the Nova client directly from the env, just to save time 1142 // looking all the stuff up 1143 novaClient := openstack.GetNovaClient(env) 1144 entity, err := novaClient.RunServer(nova.RunServerOpts{ 1145 Name: newMachineName, 1146 FlavorId: "1", // test service has 1,2,3 for flavor ids 1147 ImageId: "1", // UseTestImageData sets up images 1 and 2 1148 }) 1149 c.Assert(err, gc.IsNil) 1150 c.Assert(entity, gc.NotNil) 1151 1152 // List all servers with no filter, we should see both instances 1153 servers, err := novaClient.ListServersDetail(nova.NewFilter()) 1154 c.Assert(err, gc.IsNil) 1155 c.Assert(servers, gc.HasLen, 2) 1156 1157 insts, err = env.AllInstances() 1158 c.Assert(err, gc.IsNil) 1159 c.Check(insts, gc.HasLen, 1) 1160 } 1161 1162 func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) { 1163 env := s.Prepare(c) 1164 var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" 1165 networkId, err := openstack.ResolveNetwork(env, sampleUUID) 1166 c.Assert(err, gc.IsNil) 1167 c.Assert(networkId, gc.Equals, sampleUUID) 1168 } 1169 1170 func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) { 1171 env := s.Prepare(c) 1172 // For now this test has to cheat and use knowledge of goose internals 1173 var networkLabel = "net" 1174 var expectNetworkId = "1" 1175 networkId, err := openstack.ResolveNetwork(env, networkLabel) 1176 c.Assert(err, gc.IsNil) 1177 c.Assert(networkId, gc.Equals, expectNetworkId) 1178 } 1179 1180 func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) { 1181 env := s.Prepare(c) 1182 var notPresentNetwork = "no-network-with-this-label" 1183 networkId, err := openstack.ResolveNetwork(env, notPresentNetwork) 1184 c.Check(networkId, gc.Equals, "") 1185 c.Assert(err, gc.ErrorMatches, `No networks exist with label "no-network-with-this-label"`) 1186 } 1187 1188 // TODO(gz): TestResolveNetworkMultipleMatching when can inject new networks 1189 1190 func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) { 1191 inst, err := t.testStartInstanceAvailZone(c, "test-available") 1192 c.Assert(err, gc.IsNil) 1193 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available") 1194 } 1195 1196 func (t *localServerSuite) TestStartInstanceAvailZoneUnavailable(c *gc.C) { 1197 _, err := t.testStartInstanceAvailZone(c, "test-unavailable") 1198 c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is unavailable`) 1199 } 1200 1201 func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) { 1202 _, err := t.testStartInstanceAvailZone(c, "test-unknown") 1203 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 1204 } 1205 1206 func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) { 1207 env := t.Prepare(c) 1208 envtesting.UploadFakeTools(c, env.Storage()) 1209 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 1210 c.Assert(err, gc.IsNil) 1211 1212 params := environs.StartInstanceParams{Placement: "zone=" + zone} 1213 inst, _, _, err := testing.StartInstanceWithParams(env, "1", params, nil) 1214 return inst, err 1215 } 1216 1217 func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) { 1218 var resultZones []nova.AvailabilityZone 1219 var resultErr error 1220 t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) { 1221 return append([]nova.AvailabilityZone{}, resultZones...), resultErr 1222 }) 1223 env := t.Prepare(c).(common.ZonedEnviron) 1224 1225 resultErr = fmt.Errorf("failed to get availability zones") 1226 zones, err := env.AvailabilityZones() 1227 c.Assert(err, gc.Equals, resultErr) 1228 c.Assert(zones, gc.IsNil) 1229 1230 resultErr = nil 1231 resultZones = make([]nova.AvailabilityZone, 1) 1232 resultZones[0].Name = "whatever" 1233 zones, err = env.AvailabilityZones() 1234 c.Assert(err, gc.IsNil) 1235 c.Assert(zones, gc.HasLen, 1) 1236 c.Assert(zones[0].Name(), gc.Equals, "whatever") 1237 1238 // A successful result is cached, currently for the lifetime 1239 // of the Environ. This will change if/when we have long-lived 1240 // Environs to cut down repeated IaaS requests. 1241 resultErr = fmt.Errorf("failed to get availability zones") 1242 resultZones[0].Name = "andever" 1243 zones, err = env.AvailabilityZones() 1244 c.Assert(err, gc.IsNil) 1245 c.Assert(zones, gc.HasLen, 1) 1246 c.Assert(zones[0].Name(), gc.Equals, "whatever") 1247 } 1248 1249 func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) { 1250 var resultZones []nova.AvailabilityZone 1251 t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) { 1252 return append([]nova.AvailabilityZone{}, resultZones...), nil 1253 }) 1254 env := t.Prepare(c).(common.ZonedEnviron) 1255 resultZones = make([]nova.AvailabilityZone, 2) 1256 resultZones[0].Name = "az1" 1257 resultZones[1].Name = "az2" 1258 resultZones[0].State.Available = true 1259 resultZones[1].State.Available = false 1260 zones, err := env.AvailabilityZones() 1261 c.Assert(err, gc.IsNil) 1262 c.Assert(zones, gc.HasLen, 2) 1263 c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name) 1264 c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name) 1265 c.Assert(zones[0].Available(), jc.IsTrue) 1266 c.Assert(zones[1].Available(), jc.IsFalse) 1267 } 1268 1269 type mockBestAvailabilityZoneAllocations struct { 1270 group []instance.Id // input param 1271 result map[string][]instance.Id 1272 err error 1273 } 1274 1275 func (t *mockBestAvailabilityZoneAllocations) BestAvailabilityZoneAllocations( 1276 e common.ZonedEnviron, group []instance.Id, 1277 ) (map[string][]instance.Id, error) { 1278 t.group = group 1279 return t.result, t.err 1280 } 1281 1282 func (t *localServerSuite) TestStartInstanceDistributionParams(c *gc.C) { 1283 env := t.Prepare(c) 1284 envtesting.UploadFakeTools(c, env.Storage()) 1285 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 1286 c.Assert(err, gc.IsNil) 1287 1288 var mock mockBestAvailabilityZoneAllocations 1289 t.PatchValue(openstack.BestAvailabilityZoneAllocations, mock.BestAvailabilityZoneAllocations) 1290 1291 // no distribution group specified 1292 testing.AssertStartInstance(c, env, "1") 1293 c.Assert(mock.group, gc.HasLen, 0) 1294 1295 // distribution group specified: ensure it's passed through to BestAvailabilityZone. 1296 expectedInstances := []instance.Id{"i-0", "i-1"} 1297 params := environs.StartInstanceParams{ 1298 DistributionGroup: func() ([]instance.Id, error) { 1299 return expectedInstances, nil 1300 }, 1301 } 1302 _, _, _, err = testing.StartInstanceWithParams(env, "1", params, nil) 1303 c.Assert(err, gc.IsNil) 1304 c.Assert(mock.group, gc.DeepEquals, expectedInstances) 1305 } 1306 1307 func (t *localServerSuite) TestStartInstanceDistributionErrors(c *gc.C) { 1308 env := t.Prepare(c) 1309 envtesting.UploadFakeTools(c, env.Storage()) 1310 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 1311 c.Assert(err, gc.IsNil) 1312 1313 mock := mockBestAvailabilityZoneAllocations{ 1314 err: fmt.Errorf("BestAvailabilityZoneAllocations failed"), 1315 } 1316 t.PatchValue(openstack.BestAvailabilityZoneAllocations, mock.BestAvailabilityZoneAllocations) 1317 _, _, _, err = testing.StartInstance(env, "1") 1318 c.Assert(err, gc.Equals, mock.err) 1319 1320 mock.err = nil 1321 dgErr := fmt.Errorf("DistributionGroup failed") 1322 params := environs.StartInstanceParams{ 1323 DistributionGroup: func() ([]instance.Id, error) { 1324 return nil, dgErr 1325 }, 1326 } 1327 _, _, _, err = testing.StartInstanceWithParams(env, "1", params, nil) 1328 c.Assert(err, gc.Equals, dgErr) 1329 } 1330 1331 func (t *localServerSuite) TestStartInstanceDistribution(c *gc.C) { 1332 env := t.Prepare(c) 1333 envtesting.UploadFakeTools(c, env.Storage()) 1334 err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{}) 1335 c.Assert(err, gc.IsNil) 1336 1337 // test-available is the only available AZ, so BestAvailabilityZoneAllocations 1338 // is guaranteed to return that. 1339 inst, _ := testing.AssertStartInstance(c, env, "1") 1340 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available") 1341 }