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