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