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