github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 "errors" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/http/httptest" 13 "net/url" 14 "os" 15 "path/filepath" 16 "strings" 17 18 jujuerrors "github.com/juju/errors" 19 gitjujutesting "github.com/juju/testing" 20 jc "github.com/juju/testing/checkers" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/goose.v1/client" 23 "gopkg.in/goose.v1/identity" 24 "gopkg.in/goose.v1/nova" 25 "gopkg.in/goose.v1/testservices/hook" 26 "gopkg.in/goose.v1/testservices/identityservice" 27 "gopkg.in/goose.v1/testservices/novaservice" 28 "gopkg.in/goose.v1/testservices/openstackservice" 29 30 "github.com/juju/juju/cloudconfig/instancecfg" 31 "github.com/juju/juju/constraints" 32 "github.com/juju/juju/environs" 33 "github.com/juju/juju/environs/bootstrap" 34 "github.com/juju/juju/environs/config" 35 "github.com/juju/juju/environs/configstore" 36 "github.com/juju/juju/environs/filestorage" 37 "github.com/juju/juju/environs/imagemetadata" 38 "github.com/juju/juju/environs/jujutest" 39 "github.com/juju/juju/environs/simplestreams" 40 "github.com/juju/juju/environs/storage" 41 envtesting "github.com/juju/juju/environs/testing" 42 "github.com/juju/juju/environs/tools" 43 "github.com/juju/juju/instance" 44 "github.com/juju/juju/juju/arch" 45 "github.com/juju/juju/juju/testing" 46 "github.com/juju/juju/network" 47 "github.com/juju/juju/provider/common" 48 "github.com/juju/juju/provider/openstack" 49 "github.com/juju/juju/storage/provider/registry" 50 coretesting "github.com/juju/juju/testing" 51 "github.com/juju/juju/utils/ssh" 52 "github.com/juju/juju/version" 53 ) 54 55 type ProviderSuite struct { 56 restoreTimeouts func() 57 } 58 59 var _ = gc.Suite(&ProviderSuite{}) 60 var _ = gc.Suite(&localHTTPSServerSuite{}) 61 var _ = gc.Suite(&noSwiftSuite{}) 62 63 func (s *ProviderSuite) SetUpTest(c *gc.C) { 64 s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) 65 } 66 67 func (s *ProviderSuite) TearDownTest(c *gc.C) { 68 s.restoreTimeouts() 69 } 70 71 // Register tests to run against a test Openstack instance (service doubles). 72 func registerLocalTests() { 73 cred := &identity.Credentials{ 74 User: "fred", 75 Secrets: "secret", 76 Region: "some-region", 77 TenantName: "some tenant", 78 } 79 config := makeTestConfig(cred) 80 config["agent-version"] = coretesting.FakeVersionNumber.String() 81 config["authorized-keys"] = "fakekey" 82 gc.Suite(&localLiveSuite{ 83 LiveTests: LiveTests{ 84 cred: cred, 85 LiveTests: jujutest.LiveTests{ 86 TestConfig: config, 87 }, 88 }, 89 }) 90 gc.Suite(&localServerSuite{ 91 cred: cred, 92 Tests: jujutest.Tests{ 93 TestConfig: config, 94 }, 95 }) 96 } 97 98 // localServer is used to spin up a local Openstack service double. 99 type localServer struct { 100 Server *httptest.Server 101 Mux *http.ServeMux 102 oldHandler http.Handler 103 Nova *novaservice.Nova 104 restoreTimeouts func() 105 UseTLS bool 106 } 107 108 type newOpenstackFunc func(*http.ServeMux, *identity.Credentials, identity.AuthMode) *novaservice.Nova 109 110 func (s *localServer) start( 111 c *gc.C, cred *identity.Credentials, newOpenstackFunc newOpenstackFunc, 112 ) { 113 // Set up the HTTP server. 114 if s.UseTLS { 115 s.Server = httptest.NewTLSServer(nil) 116 } else { 117 s.Server = httptest.NewServer(nil) 118 } 119 c.Assert(s.Server, gc.NotNil) 120 s.oldHandler = s.Server.Config.Handler 121 s.Mux = http.NewServeMux() 122 s.Server.Config.Handler = s.Mux 123 cred.URL = s.Server.URL 124 c.Logf("Started service at: %v", s.Server.URL) 125 s.Nova = newOpenstackFunc(s.Mux, cred, identity.AuthUserPass) 126 s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt) 127 s.Nova.SetAvailabilityZones( 128 nova.AvailabilityZone{Name: "test-unavailable"}, 129 nova.AvailabilityZone{ 130 Name: "test-available", 131 State: nova.AvailabilityZoneState{ 132 Available: true, 133 }, 134 }, 135 ) 136 } 137 138 func (s *localServer) stop() { 139 s.Mux = nil 140 s.Server.Config.Handler = s.oldHandler 141 s.Server.Close() 142 s.restoreTimeouts() 143 } 144 145 // localLiveSuite runs tests from LiveTests using an Openstack service double. 146 type localLiveSuite struct { 147 coretesting.BaseSuite 148 LiveTests 149 srv localServer 150 } 151 152 func overrideCinderProvider(c *gc.C, s *gitjujutesting.CleanupSuite) { 153 // Override the cinder storage provider, since there is no test 154 // double for cinder. 155 old, err := registry.StorageProvider(openstack.CinderProviderType) 156 c.Assert(err, jc.ErrorIsNil) 157 registry.RegisterProvider(openstack.CinderProviderType, nil) 158 registry.RegisterProvider( 159 openstack.CinderProviderType, 160 openstack.NewCinderProvider(&mockAdapter{}), 161 ) 162 s.AddSuiteCleanup(func(*gc.C) { 163 registry.RegisterProvider(openstack.CinderProviderType, nil) 164 registry.RegisterProvider(openstack.CinderProviderType, old) 165 }) 166 } 167 168 func (s *localLiveSuite) SetUpSuite(c *gc.C) { 169 s.BaseSuite.SetUpSuite(c) 170 c.Logf("Running live tests using openstack service test double") 171 s.srv.start(c, s.cred, newFullOpenstackService) 172 s.LiveTests.SetUpSuite(c) 173 openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred) 174 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 175 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 176 overrideCinderProvider(c, &s.CleanupSuite) 177 } 178 179 func (s *localLiveSuite) TearDownSuite(c *gc.C) { 180 openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env)) 181 s.LiveTests.TearDownSuite(c) 182 s.srv.stop() 183 s.BaseSuite.TearDownSuite(c) 184 } 185 186 func (s *localLiveSuite) SetUpTest(c *gc.C) { 187 s.BaseSuite.SetUpTest(c) 188 s.LiveTests.SetUpTest(c) 189 s.PatchValue(&imagemetadata.DefaultBaseURL, "") 190 } 191 192 func (s *localLiveSuite) TearDownTest(c *gc.C) { 193 s.LiveTests.TearDownTest(c) 194 s.BaseSuite.TearDownTest(c) 195 } 196 197 // localServerSuite contains tests that run against an Openstack service double. 198 // These tests can test things that would be unreasonably slow or expensive 199 // to test on a live Openstack server. The service double is started and stopped for 200 // each test. 201 type localServerSuite struct { 202 coretesting.BaseSuite 203 jujutest.Tests 204 cred *identity.Credentials 205 srv localServer 206 env environs.Environ 207 toolsMetadataStorage storage.Storage 208 imageMetadataStorage storage.Storage 209 } 210 211 func (s *localServerSuite) SetUpSuite(c *gc.C) { 212 s.BaseSuite.SetUpSuite(c) 213 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 214 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 215 overrideCinderProvider(c, &s.CleanupSuite) 216 c.Logf("Running local tests") 217 } 218 219 func (s *localServerSuite) SetUpTest(c *gc.C) { 220 s.BaseSuite.SetUpTest(c) 221 s.srv.start(c, s.cred, newFullOpenstackService) 222 cl := client.NewClient(s.cred, identity.AuthUserPass, nil) 223 err := cl.Authenticate() 224 c.Assert(err, jc.ErrorIsNil) 225 containerURL, err := cl.MakeServiceURL("object-store", nil) 226 c.Assert(err, jc.ErrorIsNil) 227 s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{ 228 "agent-metadata-url": containerURL + "/juju-dist-test/tools", 229 "image-metadata-url": containerURL + "/juju-dist-test", 230 "auth-url": s.cred.URL, 231 }) 232 s.PatchValue(&version.Current.Number, coretesting.FakeVersionNumber) 233 s.Tests.SetUpTest(c) 234 // For testing, we create a storage instance to which is uploaded tools and image metadata. 235 s.env = s.Prepare(c) 236 s.toolsMetadataStorage = openstack.MetadataStorage(s.env) 237 // Put some fake metadata in place so that tests that are simply 238 // starting instances without any need to check if those instances 239 // are running can find the metadata. 240 envtesting.UploadFakeTools(c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream()) 241 s.imageMetadataStorage = openstack.ImageMetadataStorage(s.env) 242 openstack.UseTestImageData(s.imageMetadataStorage, s.cred) 243 } 244 245 func (s *localServerSuite) TearDownTest(c *gc.C) { 246 if s.imageMetadataStorage != nil { 247 openstack.RemoveTestImageData(s.imageMetadataStorage) 248 } 249 if s.toolsMetadataStorage != nil { 250 envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage) 251 } 252 s.Tests.TearDownTest(c) 253 s.srv.stop() 254 s.BaseSuite.TearDownTest(c) 255 } 256 257 // If the bootstrap node is configured to require a public IP address, 258 // bootstrapping fails if an address cannot be allocated. 259 func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) { 260 coretesting.SkipIfPPC64EL(c, "lp:1425242") 261 262 cleanup := s.srv.Nova.RegisterControlPoint( 263 "addFloatingIP", 264 func(sc hook.ServiceControl, args ...interface{}) error { 265 return fmt.Errorf("failed on purpose") 266 }, 267 ) 268 defer cleanup() 269 270 // Create a config that matches s.TestConfig but with use-floating-ip set to true 271 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 272 "use-floating-ip": true, 273 })) 274 c.Assert(err, jc.ErrorIsNil) 275 env, err := environs.New(cfg) 276 c.Assert(err, jc.ErrorIsNil) 277 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 278 c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*") 279 } 280 281 func (s *localServerSuite) TestAddressesWithPublicIP(c *gc.C) { 282 // Floating IP address is 10.0.0.1 283 bootstrapFinished := false 284 s.PatchValue(&common.FinishBootstrap, func(ctx environs.BootstrapContext, client ssh.Client, inst instance.Instance, instanceConfig *instancecfg.InstanceConfig) error { 285 addr, err := inst.Addresses() 286 c.Assert(err, jc.ErrorIsNil) 287 c.Assert(addr, jc.SameContents, []network.Address{ 288 {Value: "10.0.0.1", Type: "ipv4", NetworkName: "public", Scope: "public"}, 289 {Value: "127.0.0.1", Type: "ipv4", NetworkName: "private", Scope: "local-machine"}, 290 {Value: "::face::000f", Type: "hostname", NetworkName: "private", Scope: ""}, 291 {Value: "127.10.0.1", Type: "ipv4", NetworkName: "public", Scope: "public"}, 292 {Value: "::dead:beef:f00d", Type: "ipv6", NetworkName: "public", Scope: "public"}, 293 }) 294 bootstrapFinished = true 295 return nil 296 }) 297 298 // Create a config that matches s.TestConfig but with use-floating-ip set to true 299 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 300 "use-floating-ip": true, 301 })) 302 c.Assert(err, jc.ErrorIsNil) 303 env, err := environs.New(cfg) 304 c.Assert(err, jc.ErrorIsNil) 305 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 306 c.Assert(err, jc.ErrorIsNil) 307 c.Assert(bootstrapFinished, jc.IsTrue) 308 } 309 310 func (s *localServerSuite) TestAddressesWithoutPublicIP(c *gc.C) { 311 bootstrapFinished := false 312 s.PatchValue(&common.FinishBootstrap, func(ctx environs.BootstrapContext, client ssh.Client, inst instance.Instance, instanceConfig *instancecfg.InstanceConfig) error { 313 addr, err := inst.Addresses() 314 c.Assert(err, jc.ErrorIsNil) 315 c.Assert(addr, jc.SameContents, []network.Address{ 316 {Value: "127.0.0.1", Type: "ipv4", NetworkName: "private", Scope: "local-machine"}, 317 {Value: "::face::000f", Type: "hostname", NetworkName: "private", Scope: ""}, 318 {Value: "127.10.0.1", Type: "ipv4", NetworkName: "public", Scope: "public"}, 319 {Value: "::dead:beef:f00d", Type: "ipv6", NetworkName: "public", Scope: "public"}, 320 }) 321 bootstrapFinished = true 322 return nil 323 }) 324 325 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 326 "use-floating-ip": false, 327 })) 328 c.Assert(err, jc.ErrorIsNil) 329 env, err := environs.New(cfg) 330 c.Assert(err, jc.ErrorIsNil) 331 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 332 c.Assert(err, jc.ErrorIsNil) 333 c.Assert(bootstrapFinished, jc.IsTrue) 334 } 335 336 // If the environment is configured not to require a public IP address for nodes, 337 // bootstrapping and starting an instance should occur without any attempt to 338 // allocate a public address. 339 func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) { 340 cleanup := s.srv.Nova.RegisterControlPoint( 341 "addFloatingIP", 342 func(sc hook.ServiceControl, args ...interface{}) error { 343 return fmt.Errorf("add floating IP should not have been called") 344 }, 345 ) 346 defer cleanup() 347 cleanup = s.srv.Nova.RegisterControlPoint( 348 "addServerFloatingIP", 349 func(sc hook.ServiceControl, args ...interface{}) error { 350 return fmt.Errorf("add server floating IP should not have been called") 351 }, 352 ) 353 defer cleanup() 354 355 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 356 "use-floating-ip": false, 357 })) 358 c.Assert(err, jc.ErrorIsNil) 359 env, err := environs.Prepare(cfg, envtesting.BootstrapContext(c), s.ConfigStore) 360 c.Assert(err, jc.ErrorIsNil) 361 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 362 c.Assert(err, jc.ErrorIsNil) 363 inst, _ := testing.AssertStartInstance(c, env, "100") 364 err = env.StopInstances(inst.Id()) 365 c.Assert(err, jc.ErrorIsNil) 366 } 367 368 func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { 369 // Ensure amd64 tools are available, to ensure an amd64 image. 370 amd64Version := version.Current 371 amd64Version.Arch = arch.AMD64 372 for _, series := range version.SupportedSeries() { 373 amd64Version.Series = series 374 envtesting.AssertUploadFakeToolsVersions( 375 c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream(), amd64Version) 376 } 377 378 env := s.Prepare(c) 379 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 380 c.Assert(err, jc.ErrorIsNil) 381 _, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024")) 382 c.Check(*hc.Arch, gc.Equals, "amd64") 383 c.Check(*hc.Mem, gc.Equals, uint64(2048)) 384 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 385 c.Assert(hc.CpuPower, gc.IsNil) 386 } 387 388 func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) { 389 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 390 // A label that corresponds to a nova test service network 391 "network": "net", 392 })) 393 c.Assert(err, jc.ErrorIsNil) 394 env, err := environs.New(cfg) 395 c.Assert(err, jc.ErrorIsNil) 396 inst, _ := testing.AssertStartInstance(c, env, "100") 397 err = env.StopInstances(inst.Id()) 398 c.Assert(err, jc.ErrorIsNil) 399 } 400 401 func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) { 402 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 403 // A label that has no related network in the nova test service 404 "network": "no-network-with-this-label", 405 })) 406 c.Assert(err, jc.ErrorIsNil) 407 env, err := environs.New(cfg) 408 c.Assert(err, jc.ErrorIsNil) 409 inst, _, _, err := testing.StartInstance(env, "100") 410 c.Check(inst, gc.IsNil) 411 c.Assert(err, gc.ErrorMatches, "No networks exist with label .*") 412 } 413 414 func (s *localServerSuite) TestStartInstanceNetworkUnknownId(c *gc.C) { 415 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 416 // A valid UUID but no related network in the nova test service 417 "network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6", 418 })) 419 c.Assert(err, jc.ErrorIsNil) 420 env, err := environs.New(cfg) 421 c.Assert(err, jc.ErrorIsNil) 422 inst, _, _, err := testing.StartInstance(env, "100") 423 c.Check(inst, gc.IsNil) 424 c.Assert(err, gc.ErrorMatches, "cannot run instance: (\\n|.)*"+ 425 "caused by: "+ 426 "request \\(.*/servers\\) returned unexpected status: "+ 427 "404; error info: .*itemNotFound.*") 428 } 429 430 func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) { 431 novaClient := openstack.GetNovaClient(env) 432 groups, err := novaClient.ListSecurityGroups() 433 c.Assert(err, jc.ErrorIsNil) 434 for _, name := range expected { 435 found := false 436 for _, group := range groups { 437 if group.Name == name { 438 found = true 439 break 440 } 441 } 442 if !found { 443 c.Errorf("expected security group %q not found", name) 444 } 445 } 446 for _, group := range groups { 447 found := false 448 for _, name := range expected { 449 if group.Name == name { 450 found = true 451 break 452 } 453 } 454 if !found { 455 c.Errorf("existing security group %q is not expected", group.Name) 456 } 457 } 458 } 459 460 func (s *localServerSuite) TestStopInstance(c *gc.C) { 461 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 462 "firewall-mode": config.FwInstance})) 463 c.Assert(err, jc.ErrorIsNil) 464 env, err := environs.New(cfg) 465 c.Assert(err, jc.ErrorIsNil) 466 instanceName := "100" 467 inst, _ := testing.AssertStartInstance(c, env, instanceName) 468 // Openstack now has three security groups for the server, the default 469 // group, one group for the entire environment, and another for the 470 // new instance. 471 name := env.Config().Name() 472 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", name), fmt.Sprintf("juju-%v-%v", name, instanceName)}) 473 err = env.StopInstances(inst.Id()) 474 c.Assert(err, jc.ErrorIsNil) 475 // The security group for this instance is now removed. 476 assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", name)}) 477 } 478 479 // Due to bug #1300755 it can happen that the security group intended for 480 // an instance is also used as the common security group of another 481 // environment. If this is the case, the attempt to delete the instance's 482 // security group fails but StopInstance succeeds. 483 func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) { 484 coretesting.SkipIfPPC64EL(c, "lp:1425242") 485 486 // Force an error when a security group is deleted. 487 cleanup := s.srv.Nova.RegisterControlPoint( 488 "removeSecurityGroup", 489 func(sc hook.ServiceControl, args ...interface{}) error { 490 return fmt.Errorf("failed on purpose") 491 }, 492 ) 493 defer cleanup() 494 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 495 "firewall-mode": config.FwInstance})) 496 c.Assert(err, jc.ErrorIsNil) 497 env, err := environs.New(cfg) 498 c.Assert(err, jc.ErrorIsNil) 499 instanceName := "100" 500 inst, _ := testing.AssertStartInstance(c, env, instanceName) 501 name := env.Config().Name() 502 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", name), fmt.Sprintf("juju-%v-%v", name, instanceName)} 503 assertSecurityGroups(c, env, allSecurityGroups) 504 err = env.StopInstances(inst.Id()) 505 c.Assert(err, jc.ErrorIsNil) 506 assertSecurityGroups(c, env, allSecurityGroups) 507 } 508 509 func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) { 510 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 511 "firewall-mode": config.FwInstance})) 512 c.Assert(err, jc.ErrorIsNil) 513 env, err := environs.New(cfg) 514 c.Assert(err, jc.ErrorIsNil) 515 instanceName := "100" 516 testing.AssertStartInstance(c, env, instanceName) 517 name := env.Config().Name() 518 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", name), fmt.Sprintf("juju-%v-%v", name, instanceName)} 519 assertSecurityGroups(c, env, allSecurityGroups) 520 err = env.Destroy() 521 c.Check(err, jc.ErrorIsNil) 522 assertSecurityGroups(c, env, []string{"default"}) 523 } 524 525 func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) { 526 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 527 "firewall-mode": config.FwGlobal})) 528 c.Assert(err, jc.ErrorIsNil) 529 env, err := environs.New(cfg) 530 c.Assert(err, jc.ErrorIsNil) 531 instanceName := "100" 532 testing.AssertStartInstance(c, env, instanceName) 533 name := env.Config().Name() 534 allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", name), fmt.Sprintf("juju-%v-global", name)} 535 assertSecurityGroups(c, env, allSecurityGroups) 536 err = env.Destroy() 537 c.Check(err, jc.ErrorIsNil) 538 assertSecurityGroups(c, env, []string{"default"}) 539 } 540 541 var instanceGathering = []struct { 542 ids []instance.Id 543 err error 544 }{ 545 {ids: []instance.Id{"id0"}}, 546 {ids: []instance.Id{"id0", "id0"}}, 547 {ids: []instance.Id{"id0", "id1"}}, 548 {ids: []instance.Id{"id1", "id0"}}, 549 {ids: []instance.Id{"id1", "id0", "id1"}}, 550 { 551 ids: []instance.Id{""}, 552 err: environs.ErrNoInstances, 553 }, 554 { 555 ids: []instance.Id{"", ""}, 556 err: environs.ErrNoInstances, 557 }, 558 { 559 ids: []instance.Id{"", "", ""}, 560 err: environs.ErrNoInstances, 561 }, 562 { 563 ids: []instance.Id{"id0", ""}, 564 err: environs.ErrPartialInstances, 565 }, 566 { 567 ids: []instance.Id{"", "id1"}, 568 err: environs.ErrPartialInstances, 569 }, 570 { 571 ids: []instance.Id{"id0", "id1", ""}, 572 err: environs.ErrPartialInstances, 573 }, 574 { 575 ids: []instance.Id{"id0", "", "id0"}, 576 err: environs.ErrPartialInstances, 577 }, 578 { 579 ids: []instance.Id{"id0", "id0", ""}, 580 err: environs.ErrPartialInstances, 581 }, 582 { 583 ids: []instance.Id{"", "id0", "id1"}, 584 err: environs.ErrPartialInstances, 585 }, 586 } 587 588 func (s *localServerSuite) TestInstanceStatus(c *gc.C) { 589 env := s.Prepare(c) 590 // goose's test service always returns ACTIVE state. 591 inst, _ := testing.AssertStartInstance(c, env, "100") 592 c.Assert(inst.Status(), gc.Equals, nova.StatusActive) 593 err := env.StopInstances(inst.Id()) 594 c.Assert(err, jc.ErrorIsNil) 595 } 596 597 func (s *localServerSuite) TestAllInstancesFloatingIP(c *gc.C) { 598 // Create a config that matches s.TestConfig but with use-floating-ip 599 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 600 "use-floating-ip": true, 601 })) 602 c.Assert(err, jc.ErrorIsNil) 603 env, err := environs.New(cfg) 604 c.Assert(err, jc.ErrorIsNil) 605 606 inst0, _ := testing.AssertStartInstance(c, env, "100") 607 inst1, _ := testing.AssertStartInstance(c, env, "101") 608 defer func() { 609 err := env.StopInstances(inst0.Id(), inst1.Id()) 610 c.Assert(err, jc.ErrorIsNil) 611 }() 612 613 insts, err := env.AllInstances() 614 c.Assert(err, jc.ErrorIsNil) 615 for _, inst := range insts { 616 c.Assert(openstack.InstanceFloatingIP(inst).IP, gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id())) 617 } 618 } 619 620 func (s *localServerSuite) assertInstancesGathering(c *gc.C, withFloatingIP bool) { 621 // Create a config that matches s.TestConfig but with use-floating-ip 622 cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{ 623 "use-floating-ip": withFloatingIP, 624 })) 625 c.Assert(err, jc.ErrorIsNil) 626 env, err := environs.New(cfg) 627 c.Assert(err, jc.ErrorIsNil) 628 629 inst0, _ := testing.AssertStartInstance(c, env, "100") 630 id0 := inst0.Id() 631 inst1, _ := testing.AssertStartInstance(c, env, "101") 632 id1 := inst1.Id() 633 defer func() { 634 err := env.StopInstances(inst0.Id(), inst1.Id()) 635 c.Assert(err, jc.ErrorIsNil) 636 }() 637 638 for i, test := range instanceGathering { 639 c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err) 640 ids := make([]instance.Id, len(test.ids)) 641 for j, id := range test.ids { 642 switch id { 643 case "id0": 644 ids[j] = id0 645 case "id1": 646 ids[j] = id1 647 } 648 } 649 insts, err := env.Instances(ids) 650 c.Assert(err, gc.Equals, test.err) 651 if err == environs.ErrNoInstances { 652 c.Assert(insts, gc.HasLen, 0) 653 } else { 654 c.Assert(insts, gc.HasLen, len(test.ids)) 655 } 656 for j, inst := range insts { 657 if ids[j] != "" { 658 c.Assert(inst.Id(), gc.Equals, ids[j]) 659 if withFloatingIP { 660 c.Assert(openstack.InstanceFloatingIP(inst).IP, gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id())) 661 } else { 662 c.Assert(openstack.InstanceFloatingIP(inst), gc.IsNil) 663 } 664 } else { 665 c.Assert(inst, gc.IsNil) 666 } 667 } 668 } 669 } 670 671 func (s *localServerSuite) TestInstancesGathering(c *gc.C) { 672 s.assertInstancesGathering(c, false) 673 } 674 675 func (s *localServerSuite) TestInstancesGatheringWithFloatingIP(c *gc.C) { 676 s.assertInstancesGathering(c, true) 677 } 678 679 func (s *localServerSuite) TestInstancesBuildSpawning(c *gc.C) { 680 coretesting.SkipIfPPC64EL(c, "lp:1425242") 681 682 env := s.Prepare(c) 683 // HP servers are available once they are BUILD(spawning). 684 cleanup := s.srv.Nova.RegisterControlPoint( 685 "addServer", 686 func(sc hook.ServiceControl, args ...interface{}) error { 687 details := args[0].(*nova.ServerDetail) 688 details.Status = nova.StatusBuildSpawning 689 return nil 690 }, 691 ) 692 defer cleanup() 693 stateInst, _ := testing.AssertStartInstance(c, env, "100") 694 defer func() { 695 err := env.StopInstances(stateInst.Id()) 696 c.Assert(err, jc.ErrorIsNil) 697 }() 698 699 instances, err := env.Instances([]instance.Id{stateInst.Id()}) 700 701 c.Assert(err, jc.ErrorIsNil) 702 c.Assert(instances, gc.HasLen, 1) 703 c.Assert(instances[0].Status(), gc.Equals, nova.StatusBuildSpawning) 704 } 705 706 func (s *localServerSuite) TestInstancesShutoffSuspended(c *gc.C) { 707 coretesting.SkipIfPPC64EL(c, "lp:1425242") 708 709 env := s.Prepare(c) 710 cleanup := s.srv.Nova.RegisterControlPoint( 711 "addServer", 712 func(sc hook.ServiceControl, args ...interface{}) error { 713 details := args[0].(*nova.ServerDetail) 714 switch { 715 case strings.HasSuffix(details.Name, "-100"): 716 details.Status = nova.StatusShutoff 717 case strings.HasSuffix(details.Name, "-101"): 718 details.Status = nova.StatusSuspended 719 default: 720 c.Fatalf("unexpected instance details: %#v", details) 721 } 722 return nil 723 }, 724 ) 725 defer cleanup() 726 stateInst1, _ := testing.AssertStartInstance(c, env, "100") 727 stateInst2, _ := testing.AssertStartInstance(c, env, "101") 728 defer func() { 729 err := env.StopInstances(stateInst1.Id(), stateInst2.Id()) 730 c.Assert(err, jc.ErrorIsNil) 731 }() 732 733 instances, err := env.Instances([]instance.Id{stateInst1.Id(), stateInst2.Id()}) 734 735 c.Assert(err, jc.ErrorIsNil) 736 c.Assert(instances, gc.HasLen, 2) 737 c.Assert(instances[0].Status(), gc.Equals, nova.StatusShutoff) 738 c.Assert(instances[1].Status(), gc.Equals, nova.StatusSuspended) 739 } 740 741 func (s *localServerSuite) TestInstancesErrorResponse(c *gc.C) { 742 coretesting.SkipIfPPC64EL(c, "lp:1425242") 743 744 env := s.Prepare(c) 745 cleanup := s.srv.Nova.RegisterControlPoint( 746 "server", 747 func(sc hook.ServiceControl, args ...interface{}) error { 748 return fmt.Errorf("strange error not instance") 749 }, 750 ) 751 defer cleanup() 752 753 instances, err := env.Instances([]instance.Id{"1"}) 754 c.Check(instances, gc.IsNil) 755 c.Assert(err, gc.ErrorMatches, "(?s).*strange error not instance.*") 756 } 757 758 func (s *localServerSuite) TestInstancesMultiErrorResponse(c *gc.C) { 759 coretesting.SkipIfPPC64EL(c, "lp:1425242") 760 761 env := s.Prepare(c) 762 cleanup := s.srv.Nova.RegisterControlPoint( 763 "matchServers", 764 func(sc hook.ServiceControl, args ...interface{}) error { 765 return fmt.Errorf("strange error no instances") 766 }, 767 ) 768 defer cleanup() 769 770 instances, err := env.Instances([]instance.Id{"1", "2"}) 771 c.Check(instances, gc.IsNil) 772 c.Assert(err, gc.ErrorMatches, "(?s).*strange error no instances.*") 773 } 774 775 // TODO (wallyworld) - this test was copied from the ec2 provider. 776 // It should be moved to environs.jujutests.Tests. 777 func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { 778 env := s.Prepare(c) 779 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 780 c.Assert(err, jc.ErrorIsNil) 781 782 // Check that StateServerInstances returns the ID of the bootstrap machine. 783 ids, err := env.StateServerInstances() 784 c.Assert(err, jc.ErrorIsNil) 785 c.Assert(ids, gc.HasLen, 1) 786 787 // Storage should be empty; it is not used anymore. 788 stor := env.(environs.EnvironStorage).Storage() 789 entries, err := stor.List("") 790 c.Assert(err, jc.ErrorIsNil) 791 c.Assert(entries, gc.HasLen, 0) 792 793 insts, err := env.AllInstances() 794 c.Assert(err, jc.ErrorIsNil) 795 c.Assert(insts, gc.HasLen, 1) 796 c.Check(insts[0].Id(), gc.Equals, ids[0]) 797 798 addresses, err := insts[0].Addresses() 799 c.Assert(err, jc.ErrorIsNil) 800 c.Assert(addresses, gc.Not(gc.HasLen), 0) 801 802 // TODO(wallyworld) - 2013-03-01 bug=1137005 803 // The nova test double needs to be updated to support retrieving instance userData. 804 // Until then, we can't check the cloud init script was generated correctly. 805 // When we can, we should also check cloudinit for a non-manager node (as in the 806 // ec2 tests). 807 } 808 809 func (s *localServerSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) { 810 // Create a config that matches s.TestConfig but with the specified stream. 811 envAttrs := s.TestConfig 812 if stream != "" { 813 envAttrs = envAttrs.Merge(coretesting.Attrs{"image-stream": stream}) 814 } 815 cfg, err := config.New(config.NoDefaults, envAttrs) 816 c.Assert(err, jc.ErrorIsNil) 817 env, err := environs.New(cfg) 818 c.Assert(err, jc.ErrorIsNil) 819 sources, err := environs.ImageMetadataSources(env) 820 c.Assert(err, jc.ErrorIsNil) 821 c.Assert(sources, gc.HasLen, 3) 822 var urls = make([]string, len(sources)) 823 for i, source := range sources { 824 url, err := source.URL("") 825 c.Assert(err, jc.ErrorIsNil) 826 urls[i] = url 827 } 828 // The image-metadata-url ends with "/juju-dist-test/". 829 c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue) 830 // The product-streams URL ends with "/imagemetadata". 831 c.Check(strings.HasSuffix(urls[1], "/imagemetadata/"), jc.IsTrue) 832 c.Assert(urls[2], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath)) 833 } 834 835 func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) { 836 s.assertGetImageMetadataSources(c, "", "releases") 837 s.assertGetImageMetadataSources(c, "released", "releases") 838 s.assertGetImageMetadataSources(c, "daily", "daily") 839 } 840 841 func (s *localServerSuite) TestGetImageMetadataSourcesNoProductStreams(c *gc.C) { 842 s.PatchValue(openstack.MakeServiceURL, func(client.AuthenticatingClient, string, []string) (string, error) { 843 return "", errors.New("cannae do it captain") 844 }) 845 env := s.Open(c) 846 sources, err := environs.ImageMetadataSources(env) 847 c.Assert(err, jc.ErrorIsNil) 848 c.Assert(sources, gc.HasLen, 2) 849 850 // Check that data sources are in the right order 851 c.Check(sources[0].Description(), gc.Equals, "image-metadata-url") 852 c.Check(sources[1].Description(), gc.Equals, "default cloud images") 853 } 854 855 func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { 856 s.PatchValue(&tools.DefaultBaseURL, "") 857 858 env := s.Open(c) 859 sources, err := tools.GetMetadataSources(env) 860 c.Assert(err, jc.ErrorIsNil) 861 c.Assert(sources, gc.HasLen, 2) 862 var urls = make([]string, len(sources)) 863 for i, source := range sources { 864 url, err := source.URL("") 865 c.Assert(err, jc.ErrorIsNil) 866 urls[i] = url 867 } 868 // The agent-metadata-url ends with "/juju-dist-test/tools/". 869 c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue) 870 // Check that the URL from keystone parses. 871 _, err = url.Parse(urls[1]) 872 c.Assert(err, jc.ErrorIsNil) 873 } 874 875 func (s *localServerSuite) TestSupportedArchitectures(c *gc.C) { 876 env := s.Open(c) 877 a, err := env.SupportedArchitectures() 878 c.Assert(err, jc.ErrorIsNil) 879 c.Assert(a, jc.SameContents, []string{"amd64", "i386", "ppc64el"}) 880 } 881 882 func (s *localServerSuite) TestSupportsNetworking(c *gc.C) { 883 env := s.Open(c) 884 _, ok := environs.SupportsNetworking(env) 885 c.Assert(ok, jc.IsFalse) 886 } 887 888 func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) { 889 // Prevent falling over to the public datasource. 890 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 891 892 env := s.Open(c) 893 894 // An error occurs if no suitable image is found. 895 _, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G") 896 c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`) 897 } 898 899 func (s *localServerSuite) TestConstraintsValidator(c *gc.C) { 900 env := s.Open(c) 901 validator, err := env.ConstraintsValidator() 902 c.Assert(err, jc.ErrorIsNil) 903 cons := constraints.MustParse("arch=amd64 cpu-power=10") 904 unsupported, err := validator.Validate(cons) 905 c.Assert(err, jc.ErrorIsNil) 906 c.Assert(unsupported, jc.SameContents, []string{"cpu-power"}) 907 } 908 909 func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) { 910 env := s.Open(c) 911 validator, err := env.ConstraintsValidator() 912 c.Assert(err, jc.ErrorIsNil) 913 cons := constraints.MustParse("arch=arm64") 914 _, err = validator.Validate(cons) 915 c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=arm64\nvalid values are:.*") 916 cons = constraints.MustParse("instance-type=foo") 917 _, err = validator.Validate(cons) 918 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*") 919 } 920 921 func (s *localServerSuite) TestConstraintsMerge(c *gc.C) { 922 env := s.Open(c) 923 validator, err := env.ConstraintsValidator() 924 c.Assert(err, jc.ErrorIsNil) 925 consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G") 926 consB := constraints.MustParse("instance-type=m1.small") 927 cons, err := validator.Merge(consA, consB) 928 c.Assert(err, jc.ErrorIsNil) 929 c.Assert(cons, gc.DeepEquals, constraints.MustParse("instance-type=m1.small")) 930 } 931 932 func (s *localServerSuite) TestFindImageInstanceConstraint(c *gc.C) { 933 // Prevent falling over to the public datasource. 934 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 935 936 env := s.Open(c) 937 spec, err := openstack.FindInstanceSpec(env, coretesting.FakeDefaultSeries, "amd64", "instance-type=m1.tiny") 938 c.Assert(err, jc.ErrorIsNil) 939 c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny") 940 } 941 942 func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) { 943 // Prevent falling over to the public datasource. 944 s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "") 945 946 env := s.Open(c) 947 _, err := openstack.FindInstanceSpec(env, coretesting.FakeDefaultSeries, "amd64", "instance-type=m1.large") 948 c.Assert(err, gc.ErrorMatches, `no instance types in some-region matching constraints "instance-type=m1.large"`) 949 } 950 951 func (s *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) { 952 env := s.Open(c) 953 cons := constraints.MustParse("instance-type=m1.small") 954 placement := "" 955 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, cons, placement) 956 c.Assert(err, jc.ErrorIsNil) 957 } 958 959 func (s *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) { 960 env := s.Open(c) 961 cons := constraints.MustParse("instance-type=m1.large") 962 placement := "" 963 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, cons, placement) 964 c.Assert(err, gc.ErrorMatches, `invalid Openstack flavour "m1.large" specified`) 965 } 966 967 func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) { 968 env := t.Prepare(c) 969 placement := "zone=test-available" 970 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement) 971 c.Assert(err, jc.ErrorIsNil) 972 } 973 974 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) { 975 env := t.Prepare(c) 976 placement := "zone=test-unavailable" 977 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement) 978 c.Assert(err, jc.ErrorIsNil) 979 } 980 981 func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) { 982 env := t.Prepare(c) 983 placement := "zone=test-unknown" 984 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement) 985 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 986 } 987 988 func (t *localServerSuite) TestPrecheckInstanceAvailZonesUnsupported(c *gc.C) { 989 t.srv.Nova.SetAvailabilityZones() // no availability zone support 990 env := t.Prepare(c) 991 placement := "zone=test-unknown" 992 err := env.PrecheckInstance(coretesting.FakeDefaultSeries, constraints.Value{}, placement) 993 c.Assert(err, jc.Satisfies, jujuerrors.IsNotImplemented) 994 } 995 996 func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) { 997 env := s.Open(c) 998 params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region") 999 c.Assert(err, jc.ErrorIsNil) 1000 params.Sources, err = environs.ImageMetadataSources(env) 1001 c.Assert(err, jc.ErrorIsNil) 1002 params.Series = "raring" 1003 image_ids, _, err := imagemetadata.ValidateImageMetadata(params) 1004 c.Assert(err, jc.ErrorIsNil) 1005 c.Assert(image_ids, jc.SameContents, []string{"id-y"}) 1006 } 1007 1008 func (s *localServerSuite) TestImageMetadataSourceOrder(c *gc.C) { 1009 src := func(env environs.Environ) (simplestreams.DataSource, error) { 1010 return simplestreams.NewURLDataSource("my datasource", "bar", false), nil 1011 } 1012 environs.RegisterUserImageDataSourceFunc("my func", src) 1013 env := s.Open(c) 1014 sources, err := environs.ImageMetadataSources(env) 1015 c.Assert(err, jc.ErrorIsNil) 1016 var sourceIds []string 1017 for _, s := range sources { 1018 sourceIds = append(sourceIds, s.Description()) 1019 } 1020 c.Assert(sourceIds, jc.DeepEquals, []string{"image-metadata-url", "my datasource", "keystone catalog", "default cloud images"}) 1021 } 1022 1023 func (s *localServerSuite) TestRemoveAll(c *gc.C) { 1024 env := s.Prepare(c) 1025 stor := env.(environs.EnvironStorage).Storage() 1026 for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") { 1027 content := []byte{a} 1028 name := string(content) 1029 err := stor.Put(name, bytes.NewBuffer(content), 1030 int64(len(content))) 1031 c.Assert(err, jc.ErrorIsNil) 1032 } 1033 reader, err := storage.Get(stor, "a") 1034 c.Assert(err, jc.ErrorIsNil) 1035 allContent, err := ioutil.ReadAll(reader) 1036 c.Assert(err, jc.ErrorIsNil) 1037 c.Assert(string(allContent), gc.Equals, "a") 1038 err = stor.RemoveAll() 1039 c.Assert(err, jc.ErrorIsNil) 1040 _, err = storage.Get(stor, "a") 1041 c.Assert(err, gc.NotNil) 1042 } 1043 1044 func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) { 1045 env := s.Prepare(c) 1046 stor := env.(environs.EnvironStorage).Storage() 1047 // 6*26 = 156 items 1048 for _, a := range []byte("abcdef") { 1049 for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") { 1050 content := []byte{a, b} 1051 name := string(content) 1052 err := stor.Put(name, bytes.NewBuffer(content), 1053 int64(len(content))) 1054 c.Assert(err, jc.ErrorIsNil) 1055 } 1056 } 1057 reader, err := storage.Get(stor, "ab") 1058 c.Assert(err, jc.ErrorIsNil) 1059 allContent, err := ioutil.ReadAll(reader) 1060 c.Assert(err, jc.ErrorIsNil) 1061 c.Assert(string(allContent), gc.Equals, "ab") 1062 err = stor.RemoveAll() 1063 c.Assert(err, jc.ErrorIsNil) 1064 _, err = storage.Get(stor, "ab") 1065 c.Assert(err, gc.NotNil) 1066 } 1067 1068 // TestEnsureGroup checks that when creating a duplicate security group, the existing group is 1069 // returned and the existing rules have been left as is. 1070 func (s *localServerSuite) TestEnsureGroup(c *gc.C) { 1071 env := s.Prepare(c) 1072 rule := []nova.RuleInfo{ 1073 { 1074 IPProtocol: "tcp", 1075 FromPort: 22, 1076 ToPort: 22, 1077 }, 1078 } 1079 1080 assertRule := func(group nova.SecurityGroup) { 1081 c.Check(len(group.Rules), gc.Equals, 1) 1082 c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp") 1083 c.Check(*group.Rules[0].FromPort, gc.Equals, 22) 1084 c.Check(*group.Rules[0].ToPort, gc.Equals, 22) 1085 } 1086 1087 group, err := openstack.EnsureGroup(env, "test group", rule) 1088 c.Assert(err, jc.ErrorIsNil) 1089 c.Assert(group.Name, gc.Equals, "test group") 1090 assertRule(group) 1091 id := group.Id 1092 // Do it again and check that the existing group is returned. 1093 anotherRule := []nova.RuleInfo{ 1094 { 1095 IPProtocol: "tcp", 1096 FromPort: 1, 1097 ToPort: 65535, 1098 }, 1099 } 1100 group, err = openstack.EnsureGroup(env, "test group", anotherRule) 1101 c.Assert(err, jc.ErrorIsNil) 1102 c.Check(group.Id, gc.Equals, id) 1103 c.Assert(group.Name, gc.Equals, "test group") 1104 assertRule(group) 1105 } 1106 1107 // localHTTPSServerSuite contains tests that run against an Openstack service 1108 // double connected on an HTTPS port with a self-signed certificate. This 1109 // service is set up and torn down for every test. This should only test 1110 // things that depend on the HTTPS connection, all other functional tests on a 1111 // local connection should be in localServerSuite 1112 type localHTTPSServerSuite struct { 1113 coretesting.BaseSuite 1114 attrs map[string]interface{} 1115 cred *identity.Credentials 1116 srv localServer 1117 env environs.Environ 1118 } 1119 1120 func (s *localHTTPSServerSuite) SetUpSuite(c *gc.C) { 1121 s.BaseSuite.SetUpSuite(c) 1122 overrideCinderProvider(c, &s.CleanupSuite) 1123 } 1124 1125 func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} { 1126 attrs := makeTestConfig(s.cred) 1127 attrs["agent-version"] = coretesting.FakeVersionNumber.String() 1128 attrs["authorized-keys"] = "fakekey" 1129 // In order to set up and tear down the environment properly, we must 1130 // disable hostname verification 1131 attrs["ssl-hostname-verification"] = false 1132 attrs["auth-url"] = s.cred.URL 1133 // Now connect and set up test-local tools and image-metadata URLs 1134 cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil) 1135 err := cl.Authenticate() 1136 c.Assert(err, jc.ErrorIsNil) 1137 containerURL, err := cl.MakeServiceURL("object-store", nil) 1138 c.Assert(err, jc.ErrorIsNil) 1139 c.Check(containerURL[:8], gc.Equals, "https://") 1140 attrs["agent-metadata-url"] = containerURL + "/juju-dist-test/tools" 1141 c.Logf("Set agent-metadata-url=%q", attrs["agent-metadata-url"]) 1142 attrs["image-metadata-url"] = containerURL + "/juju-dist-test" 1143 c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"]) 1144 return attrs 1145 } 1146 1147 func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) { 1148 s.BaseSuite.SetUpTest(c) 1149 s.PatchValue(&version.Current.Number, coretesting.FakeVersionNumber) 1150 s.srv.UseTLS = true 1151 cred := &identity.Credentials{ 1152 User: "fred", 1153 Secrets: "secret", 1154 Region: "some-region", 1155 TenantName: "some tenant", 1156 } 1157 // Note: start() will change cred.URL to point to s.srv.Server.URL 1158 s.srv.start(c, cred, newFullOpenstackService) 1159 s.cred = cred 1160 attrs := s.createConfigAttrs(c) 1161 c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://") 1162 cfg, err := config.New(config.NoDefaults, attrs) 1163 c.Assert(err, jc.ErrorIsNil) 1164 s.env, err = environs.Prepare(cfg, envtesting.BootstrapContext(c), configstore.NewMem()) 1165 c.Assert(err, jc.ErrorIsNil) 1166 s.attrs = s.env.Config().AllAttrs() 1167 } 1168 1169 func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) { 1170 if s.env != nil { 1171 err := s.env.Destroy() 1172 c.Check(err, jc.ErrorIsNil) 1173 s.env = nil 1174 } 1175 s.srv.stop() 1176 s.BaseSuite.TearDownTest(c) 1177 } 1178 1179 func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) { 1180 coretesting.SkipIfPPC64EL(c, "lp:1425242") 1181 1182 // If you don't have ssl-hostname-verification set to false, then we 1183 // fail to connect to the environment. Copy the attrs used by SetUp and 1184 // force hostname verification. 1185 newattrs := make(map[string]interface{}, len(s.attrs)) 1186 for k, v := range s.attrs { 1187 newattrs[k] = v 1188 } 1189 newattrs["ssl-hostname-verification"] = true 1190 env, err := environs.NewFromAttrs(newattrs) 1191 c.Assert(err, jc.ErrorIsNil) 1192 err = env.(environs.EnvironStorage).Storage().Put("test-name", strings.NewReader("content"), 7) 1193 c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") 1194 // However, it works just fine if you use the one with the credentials set 1195 err = s.env.(environs.EnvironStorage).Storage().Put("test-name", strings.NewReader("content"), 7) 1196 c.Assert(err, jc.ErrorIsNil) 1197 _, err = env.(environs.EnvironStorage).Storage().Get("test-name") 1198 c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority") 1199 reader, err := s.env.(environs.EnvironStorage).Storage().Get("test-name") 1200 c.Assert(err, jc.ErrorIsNil) 1201 contents, err := ioutil.ReadAll(reader) 1202 c.Assert(string(contents), gc.Equals, "content") 1203 } 1204 1205 func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) { 1206 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 1207 defer restoreFinishBootstrap() 1208 1209 // For testing, we create a storage instance to which is uploaded tools and image metadata. 1210 metadataStorage := openstack.MetadataStorage(s.env) 1211 url, err := metadataStorage.URL("") 1212 c.Assert(err, jc.ErrorIsNil) 1213 c.Logf("Generating fake tools for: %v", url) 1214 envtesting.UploadFakeTools(c, metadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream()) 1215 defer envtesting.RemoveFakeTools(c, metadataStorage, s.env.Config().AgentStream()) 1216 openstack.UseTestImageData(metadataStorage, s.cred) 1217 defer openstack.RemoveTestImageData(metadataStorage) 1218 1219 err = bootstrap.Bootstrap(envtesting.BootstrapContext(c), s.env, bootstrap.BootstrapParams{}) 1220 c.Assert(err, jc.ErrorIsNil) 1221 } 1222 1223 func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) { 1224 // Setup a custom URL for image metadata 1225 customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata") 1226 customURL, err := customStorage.URL("") 1227 c.Assert(err, jc.ErrorIsNil) 1228 c.Check(customURL[:8], gc.Equals, "https://") 1229 1230 config, err := s.env.Config().Apply( 1231 map[string]interface{}{"image-metadata-url": customURL}, 1232 ) 1233 c.Assert(err, jc.ErrorIsNil) 1234 err = s.env.SetConfig(config) 1235 c.Assert(err, jc.ErrorIsNil) 1236 sources, err := environs.ImageMetadataSources(s.env) 1237 c.Assert(err, jc.ErrorIsNil) 1238 c.Assert(sources, gc.HasLen, 3) 1239 1240 // Make sure there is something to download from each location 1241 metadata := "metadata-content" 1242 metadataStorage := openstack.ImageMetadataStorage(s.env) 1243 err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata))) 1244 c.Assert(err, jc.ErrorIsNil) 1245 1246 custom := "custom-content" 1247 err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) 1248 c.Assert(err, jc.ErrorIsNil) 1249 1250 // Produce map of data sources keyed on description 1251 mappedSources := make(map[string]simplestreams.DataSource, len(sources)) 1252 for i, s := range sources { 1253 c.Logf("datasource %d: %+v", i, s) 1254 mappedSources[s.Description()] = s 1255 } 1256 1257 // Read from the Config entry's image-metadata-url 1258 contentReader, url, err := mappedSources["image-metadata-url"].Fetch(custom) 1259 c.Assert(err, jc.ErrorIsNil) 1260 defer contentReader.Close() 1261 content, err := ioutil.ReadAll(contentReader) 1262 c.Assert(err, jc.ErrorIsNil) 1263 c.Assert(string(content), gc.Equals, custom) 1264 c.Check(url[:8], gc.Equals, "https://") 1265 1266 // Check the entry we got from keystone 1267 contentReader, url, err = mappedSources["keystone catalog"].Fetch(metadata) 1268 c.Assert(err, jc.ErrorIsNil) 1269 defer contentReader.Close() 1270 content, err = ioutil.ReadAll(contentReader) 1271 c.Assert(err, jc.ErrorIsNil) 1272 c.Assert(string(content), gc.Equals, metadata) 1273 c.Check(url[:8], gc.Equals, "https://") 1274 // Verify that we are pointing at exactly where metadataStorage thinks we are 1275 metaURL, err := metadataStorage.URL(metadata) 1276 c.Assert(err, jc.ErrorIsNil) 1277 c.Check(url, gc.Equals, metaURL) 1278 1279 } 1280 1281 func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) { 1282 // Setup a custom URL for image metadata 1283 customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata") 1284 customURL, err := customStorage.URL("") 1285 c.Assert(err, jc.ErrorIsNil) 1286 c.Check(customURL[:8], gc.Equals, "https://") 1287 1288 config, err := s.env.Config().Apply( 1289 map[string]interface{}{"agent-metadata-url": customURL}, 1290 ) 1291 c.Assert(err, jc.ErrorIsNil) 1292 err = s.env.SetConfig(config) 1293 c.Assert(err, jc.ErrorIsNil) 1294 sources, err := tools.GetMetadataSources(s.env) 1295 c.Assert(err, jc.ErrorIsNil) 1296 c.Assert(sources, gc.HasLen, 3) 1297 1298 // Make sure there is something to download from each location 1299 1300 keystone := "keystone-tools-content" 1301 // The keystone entry just points at the root of the Swift storage, and 1302 // we have to create a container to upload any data. So we just point 1303 // into a subdirectory for the data we are downloading 1304 keystoneContainer := "tools-test" 1305 keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test") 1306 err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone))) 1307 c.Assert(err, jc.ErrorIsNil) 1308 1309 custom := "custom-tools-content" 1310 err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom))) 1311 c.Assert(err, jc.ErrorIsNil) 1312 1313 // Read from the Config entry's agent-metadata-url 1314 contentReader, url, err := sources[0].Fetch(custom) 1315 c.Assert(err, jc.ErrorIsNil) 1316 defer contentReader.Close() 1317 content, err := ioutil.ReadAll(contentReader) 1318 c.Assert(err, jc.ErrorIsNil) 1319 c.Assert(string(content), gc.Equals, custom) 1320 c.Check(url[:8], gc.Equals, "https://") 1321 1322 // Check the entry we got from keystone 1323 // Now fetch the data, and verify the contents. 1324 contentReader, url, err = sources[1].Fetch(keystoneContainer + "/" + keystone) 1325 c.Assert(err, jc.ErrorIsNil) 1326 defer contentReader.Close() 1327 content, err = ioutil.ReadAll(contentReader) 1328 c.Assert(err, jc.ErrorIsNil) 1329 c.Assert(string(content), gc.Equals, keystone) 1330 c.Check(url[:8], gc.Equals, "https://") 1331 keystoneURL, err := keystoneStorage.URL(keystone) 1332 c.Assert(err, jc.ErrorIsNil) 1333 c.Check(url, gc.Equals, keystoneURL) 1334 1335 // We *don't* test Fetch for sources[3] because it points to 1336 // streams.canonical.com 1337 } 1338 1339 func (s *localServerSuite) TestRemoveBlankContainer(c *gc.C) { 1340 storage := openstack.BlankContainerStorage() 1341 err := storage.Remove("some-file") 1342 c.Assert(err, gc.ErrorMatches, `cannot remove "some-file": swift container name is empty`) 1343 } 1344 1345 func (s *localServerSuite) TestAllInstancesIgnoresOtherMachines(c *gc.C) { 1346 env := s.Prepare(c) 1347 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1348 c.Assert(err, jc.ErrorIsNil) 1349 1350 // Check that we see 1 instance in the environment 1351 insts, err := env.AllInstances() 1352 c.Assert(err, jc.ErrorIsNil) 1353 c.Check(insts, gc.HasLen, 1) 1354 1355 // Now start a machine 'manually' in the same account, with a similar 1356 // but not matching name, and ensure it isn't seen by AllInstances 1357 // See bug #1257481, for how similar names were causing them to get 1358 // listed (and thus destroyed) at the wrong time 1359 existingEnvName := s.TestConfig["name"] 1360 newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingEnvName) 1361 1362 // We grab the Nova client directly from the env, just to save time 1363 // looking all the stuff up 1364 novaClient := openstack.GetNovaClient(env) 1365 entity, err := novaClient.RunServer(nova.RunServerOpts{ 1366 Name: newMachineName, 1367 FlavorId: "1", // test service has 1,2,3 for flavor ids 1368 ImageId: "1", // UseTestImageData sets up images 1 and 2 1369 }) 1370 c.Assert(err, jc.ErrorIsNil) 1371 c.Assert(entity, gc.NotNil) 1372 1373 // List all servers with no filter, we should see both instances 1374 servers, err := novaClient.ListServersDetail(nova.NewFilter()) 1375 c.Assert(err, jc.ErrorIsNil) 1376 c.Assert(servers, gc.HasLen, 2) 1377 1378 insts, err = env.AllInstances() 1379 c.Assert(err, jc.ErrorIsNil) 1380 c.Check(insts, gc.HasLen, 1) 1381 } 1382 1383 func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) { 1384 env := s.Prepare(c) 1385 var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" 1386 networkId, err := openstack.ResolveNetwork(env, sampleUUID) 1387 c.Assert(err, jc.ErrorIsNil) 1388 c.Assert(networkId, gc.Equals, sampleUUID) 1389 } 1390 1391 func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) { 1392 env := s.Prepare(c) 1393 // For now this test has to cheat and use knowledge of goose internals 1394 var networkLabel = "net" 1395 var expectNetworkId = "1" 1396 networkId, err := openstack.ResolveNetwork(env, networkLabel) 1397 c.Assert(err, jc.ErrorIsNil) 1398 c.Assert(networkId, gc.Equals, expectNetworkId) 1399 } 1400 1401 func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) { 1402 env := s.Prepare(c) 1403 var notPresentNetwork = "no-network-with-this-label" 1404 networkId, err := openstack.ResolveNetwork(env, notPresentNetwork) 1405 c.Check(networkId, gc.Equals, "") 1406 c.Assert(err, gc.ErrorMatches, `No networks exist with label "no-network-with-this-label"`) 1407 } 1408 1409 // TODO(gz): TestResolveNetworkMultipleMatching when can inject new networks 1410 1411 func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) { 1412 inst, err := t.testStartInstanceAvailZone(c, "test-available") 1413 c.Assert(err, jc.ErrorIsNil) 1414 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available") 1415 } 1416 1417 func (t *localServerSuite) TestStartInstanceAvailZoneUnavailable(c *gc.C) { 1418 _, err := t.testStartInstanceAvailZone(c, "test-unavailable") 1419 c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is unavailable`) 1420 } 1421 1422 func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) { 1423 _, err := t.testStartInstanceAvailZone(c, "test-unknown") 1424 c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`) 1425 } 1426 1427 func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) { 1428 env := t.Prepare(c) 1429 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1430 c.Assert(err, jc.ErrorIsNil) 1431 1432 params := environs.StartInstanceParams{Placement: "zone=" + zone} 1433 result, err := testing.StartInstanceWithParams(env, "1", params, nil) 1434 if err != nil { 1435 return nil, err 1436 } 1437 return result.Instance, nil 1438 } 1439 1440 func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) { 1441 var resultZones []nova.AvailabilityZone 1442 var resultErr error 1443 t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) { 1444 return append([]nova.AvailabilityZone{}, resultZones...), resultErr 1445 }) 1446 env := t.Prepare(c).(common.ZonedEnviron) 1447 1448 resultErr = fmt.Errorf("failed to get availability zones") 1449 zones, err := env.AvailabilityZones() 1450 c.Assert(err, gc.Equals, resultErr) 1451 c.Assert(zones, gc.IsNil) 1452 1453 resultErr = nil 1454 resultZones = make([]nova.AvailabilityZone, 1) 1455 resultZones[0].Name = "whatever" 1456 zones, err = env.AvailabilityZones() 1457 c.Assert(err, jc.ErrorIsNil) 1458 c.Assert(zones, gc.HasLen, 1) 1459 c.Assert(zones[0].Name(), gc.Equals, "whatever") 1460 1461 // A successful result is cached, currently for the lifetime 1462 // of the Environ. This will change if/when we have long-lived 1463 // Environs to cut down repeated IaaS requests. 1464 resultErr = fmt.Errorf("failed to get availability zones") 1465 resultZones[0].Name = "andever" 1466 zones, err = env.AvailabilityZones() 1467 c.Assert(err, jc.ErrorIsNil) 1468 c.Assert(zones, gc.HasLen, 1) 1469 c.Assert(zones[0].Name(), gc.Equals, "whatever") 1470 } 1471 1472 func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) { 1473 var resultZones []nova.AvailabilityZone 1474 t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) { 1475 return append([]nova.AvailabilityZone{}, resultZones...), nil 1476 }) 1477 env := t.Prepare(c).(common.ZonedEnviron) 1478 resultZones = make([]nova.AvailabilityZone, 2) 1479 resultZones[0].Name = "az1" 1480 resultZones[1].Name = "az2" 1481 resultZones[0].State.Available = true 1482 resultZones[1].State.Available = false 1483 zones, err := env.AvailabilityZones() 1484 c.Assert(err, jc.ErrorIsNil) 1485 c.Assert(zones, gc.HasLen, 2) 1486 c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name) 1487 c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name) 1488 c.Assert(zones[0].Available(), jc.IsTrue) 1489 c.Assert(zones[1].Available(), jc.IsFalse) 1490 } 1491 1492 type mockAvailabilityZoneAllocations struct { 1493 group []instance.Id // input param 1494 result []common.AvailabilityZoneInstances 1495 err error 1496 } 1497 1498 func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations( 1499 e common.ZonedEnviron, group []instance.Id, 1500 ) ([]common.AvailabilityZoneInstances, error) { 1501 t.group = group 1502 return t.result, t.err 1503 } 1504 1505 func (t *localServerSuite) TestStartInstanceDistributionParams(c *gc.C) { 1506 env := t.Prepare(c) 1507 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1508 c.Assert(err, jc.ErrorIsNil) 1509 1510 var mock mockAvailabilityZoneAllocations 1511 t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 1512 1513 // no distribution group specified 1514 testing.AssertStartInstance(c, env, "1") 1515 c.Assert(mock.group, gc.HasLen, 0) 1516 1517 // distribution group specified: ensure it's passed through to AvailabilityZone. 1518 expectedInstances := []instance.Id{"i-0", "i-1"} 1519 params := environs.StartInstanceParams{ 1520 DistributionGroup: func() ([]instance.Id, error) { 1521 return expectedInstances, nil 1522 }, 1523 } 1524 _, err = testing.StartInstanceWithParams(env, "1", params, nil) 1525 c.Assert(err, jc.ErrorIsNil) 1526 c.Assert(mock.group, gc.DeepEquals, expectedInstances) 1527 } 1528 1529 func (t *localServerSuite) TestStartInstanceDistributionErrors(c *gc.C) { 1530 env := t.Prepare(c) 1531 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1532 c.Assert(err, jc.ErrorIsNil) 1533 1534 mock := mockAvailabilityZoneAllocations{ 1535 err: fmt.Errorf("AvailabilityZoneAllocations failed"), 1536 } 1537 t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 1538 _, _, _, err = testing.StartInstance(env, "1") 1539 c.Assert(jujuerrors.Cause(err), gc.Equals, mock.err) 1540 1541 mock.err = nil 1542 dgErr := fmt.Errorf("DistributionGroup failed") 1543 params := environs.StartInstanceParams{ 1544 DistributionGroup: func() ([]instance.Id, error) { 1545 return nil, dgErr 1546 }, 1547 } 1548 _, err = testing.StartInstanceWithParams(env, "1", params, nil) 1549 c.Assert(jujuerrors.Cause(err), gc.Equals, dgErr) 1550 } 1551 1552 func (t *localServerSuite) TestStartInstanceDistribution(c *gc.C) { 1553 env := t.Prepare(c) 1554 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1555 c.Assert(err, jc.ErrorIsNil) 1556 1557 // test-available is the only available AZ, so AvailabilityZoneAllocations 1558 // is guaranteed to return that. 1559 inst, _ := testing.AssertStartInstance(c, env, "1") 1560 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available") 1561 } 1562 1563 func (t *localServerSuite) TestStartInstancePicksValidZoneForHost(c *gc.C) { 1564 coretesting.SkipIfPPC64EL(c, "lp:1425242") 1565 1566 t.srv.Nova.SetAvailabilityZones( 1567 // bootstrap node will be on az1. 1568 nova.AvailabilityZone{ 1569 Name: "az1", 1570 State: nova.AvailabilityZoneState{ 1571 Available: true, 1572 }, 1573 }, 1574 // az2 will be made to return an error. 1575 nova.AvailabilityZone{ 1576 Name: "az2", 1577 State: nova.AvailabilityZoneState{ 1578 Available: true, 1579 }, 1580 }, 1581 // az3 will be valid to host an instance. 1582 nova.AvailabilityZone{ 1583 Name: "az3", 1584 State: nova.AvailabilityZoneState{ 1585 Available: true, 1586 }, 1587 }, 1588 ) 1589 1590 env := t.Prepare(c) 1591 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1592 c.Assert(err, jc.ErrorIsNil) 1593 1594 cleanup := t.srv.Nova.RegisterControlPoint( 1595 "addServer", 1596 func(sc hook.ServiceControl, args ...interface{}) error { 1597 serverDetail := args[0].(*nova.ServerDetail) 1598 if serverDetail.AvailabilityZone == "az2" { 1599 return fmt.Errorf("No valid host was found") 1600 } 1601 return nil 1602 }, 1603 ) 1604 defer cleanup() 1605 inst, _ := testing.AssertStartInstance(c, env, "1") 1606 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "az3") 1607 } 1608 1609 func (t *localServerSuite) TestStartInstanceWithUnknownAZError(c *gc.C) { 1610 coretesting.SkipIfPPC64EL(c, "lp:1425242") 1611 1612 t.srv.Nova.SetAvailabilityZones( 1613 // bootstrap node will be on az1. 1614 nova.AvailabilityZone{ 1615 Name: "az1", 1616 State: nova.AvailabilityZoneState{ 1617 Available: true, 1618 }, 1619 }, 1620 // az2 will be made to return an unknown error. 1621 nova.AvailabilityZone{ 1622 Name: "az2", 1623 State: nova.AvailabilityZoneState{ 1624 Available: true, 1625 }, 1626 }, 1627 ) 1628 1629 env := t.Prepare(c) 1630 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1631 c.Assert(err, jc.ErrorIsNil) 1632 1633 cleanup := t.srv.Nova.RegisterControlPoint( 1634 "addServer", 1635 func(sc hook.ServiceControl, args ...interface{}) error { 1636 serverDetail := args[0].(*nova.ServerDetail) 1637 if serverDetail.AvailabilityZone == "az2" { 1638 return fmt.Errorf("Some unknown error") 1639 } 1640 return nil 1641 }, 1642 ) 1643 defer cleanup() 1644 _, _, _, err = testing.StartInstance(env, "1") 1645 c.Assert(err, gc.ErrorMatches, "(?s).*Some unknown error.*") 1646 } 1647 1648 func (t *localServerSuite) TestStartInstanceDistributionAZNotImplemented(c *gc.C) { 1649 env := t.Prepare(c) 1650 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1651 c.Assert(err, jc.ErrorIsNil) 1652 1653 mock := mockAvailabilityZoneAllocations{ 1654 err: jujuerrors.NotImplementedf("availability zones"), 1655 } 1656 t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations) 1657 1658 // Instance will be created without an availability zone specified. 1659 inst, _ := testing.AssertStartInstance(c, env, "1") 1660 c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "") 1661 } 1662 1663 func (t *localServerSuite) TestInstanceTags(c *gc.C) { 1664 env := t.Prepare(c) 1665 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1666 c.Assert(err, jc.ErrorIsNil) 1667 1668 instances, err := env.AllInstances() 1669 c.Assert(err, jc.ErrorIsNil) 1670 c.Assert(instances, gc.HasLen, 1) 1671 1672 c.Assert( 1673 openstack.InstanceServerDetail(instances[0]).Metadata, 1674 jc.DeepEquals, 1675 map[string]string{ 1676 "juju-env-uuid": coretesting.EnvironmentTag.Id(), 1677 "juju-is-state": "true", 1678 }, 1679 ) 1680 } 1681 1682 func (t *localServerSuite) TestTagInstance(c *gc.C) { 1683 env := t.Prepare(c) 1684 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{}) 1685 c.Assert(err, jc.ErrorIsNil) 1686 1687 assertMetadata := func(extraKey, extraValue string) { 1688 // Refresh instance 1689 instances, err := env.AllInstances() 1690 c.Assert(err, jc.ErrorIsNil) 1691 c.Assert(instances, gc.HasLen, 1) 1692 c.Assert( 1693 openstack.InstanceServerDetail(instances[0]).Metadata, 1694 jc.DeepEquals, 1695 map[string]string{ 1696 "juju-env-uuid": coretesting.EnvironmentTag.Id(), 1697 "juju-is-state": "true", 1698 extraKey: extraValue, 1699 }, 1700 ) 1701 } 1702 1703 instances, err := env.AllInstances() 1704 c.Assert(err, jc.ErrorIsNil) 1705 c.Assert(instances, gc.HasLen, 1) 1706 1707 extraKey := "extra-k" 1708 extraValue := "extra-v" 1709 err = env.(environs.InstanceTagger).TagInstance( 1710 instances[0].Id(), map[string]string{extraKey: extraValue}, 1711 ) 1712 c.Assert(err, jc.ErrorIsNil) 1713 assertMetadata(extraKey, extraValue) 1714 1715 // Ensure that a second call updates existing tags. 1716 extraValue = "extra-v2" 1717 err = env.(environs.InstanceTagger).TagInstance( 1718 instances[0].Id(), map[string]string{extraKey: extraValue}, 1719 ) 1720 c.Assert(err, jc.ErrorIsNil) 1721 assertMetadata(extraKey, extraValue) 1722 } 1723 1724 // noSwiftSuite contains tests that run against an OpenStack service double 1725 // that lacks Swift. 1726 type noSwiftSuite struct { 1727 coretesting.BaseSuite 1728 cred *identity.Credentials 1729 srv localServer 1730 env environs.Environ 1731 } 1732 1733 func (s *noSwiftSuite) SetUpSuite(c *gc.C) { 1734 s.BaseSuite.SetUpSuite(c) 1735 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 1736 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 1737 } 1738 1739 func (s *noSwiftSuite) SetUpTest(c *gc.C) { 1740 s.BaseSuite.SetUpTest(c) 1741 s.cred = &identity.Credentials{ 1742 User: "fred", 1743 Secrets: "secret", 1744 Region: "some-region", 1745 TenantName: "some tenant", 1746 } 1747 s.srv.start(c, s.cred, newNovaOnlyOpenstackService) 1748 1749 attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{ 1750 "name": "sample-no-swift", 1751 "type": "openstack", 1752 "auth-mode": "userpass", 1753 "control-bucket": "juju-test-no-swift", 1754 "username": s.cred.User, 1755 "password": s.cred.Secrets, 1756 "region": s.cred.Region, 1757 "auth-url": s.cred.URL, 1758 "tenant-name": s.cred.TenantName, 1759 "agent-version": coretesting.FakeVersionNumber.String(), 1760 "authorized-keys": "fakekey", 1761 }) 1762 s.PatchValue(&version.Current.Number, coretesting.FakeVersionNumber) 1763 // Serve fake tools and image metadata using "filestorage", 1764 // rather than Swift as the rest of the tests do. 1765 storageDir := c.MkDir() 1766 imagesDir := filepath.Join(storageDir, "images") 1767 toolsDir := filepath.Join(storageDir, "tools") 1768 for _, dir := range []string{imagesDir, toolsDir} { 1769 err := os.MkdirAll(dir, 0755) 1770 c.Assert(err, jc.ErrorIsNil) 1771 } 1772 toolsStorage, err := filestorage.NewFileStorageWriter(storageDir) 1773 c.Assert(err, jc.ErrorIsNil) 1774 envtesting.UploadFakeTools(c, toolsStorage, "released", "released") 1775 s.PatchValue(&tools.DefaultBaseURL, storageDir) 1776 imageStorage, err := filestorage.NewFileStorageWriter(imagesDir) 1777 openstack.UseTestImageData(imageStorage, s.cred) 1778 s.PatchValue(&imagemetadata.DefaultBaseURL, storageDir) 1779 1780 cfg, err := config.New(config.NoDefaults, attrs) 1781 c.Assert(err, jc.ErrorIsNil) 1782 configStore := configstore.NewMem() 1783 env, err := environs.Prepare(cfg, envtesting.BootstrapContext(c), configStore) 1784 c.Assert(err, jc.ErrorIsNil) 1785 s.env = env 1786 } 1787 1788 func (s *noSwiftSuite) TearDownTest(c *gc.C) { 1789 s.srv.stop() 1790 s.BaseSuite.TearDownTest(c) 1791 } 1792 1793 func (s *noSwiftSuite) TestBootstrap(c *gc.C) { 1794 err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), s.env, bootstrap.BootstrapParams{}) 1795 c.Assert(err, jc.ErrorIsNil) 1796 } 1797 1798 func newFullOpenstackService(mux *http.ServeMux, cred *identity.Credentials, auth identity.AuthMode) *novaservice.Nova { 1799 service := openstackservice.New(cred, auth) 1800 service.SetupHTTP(mux) 1801 return service.Nova 1802 } 1803 1804 func newNovaOnlyOpenstackService(mux *http.ServeMux, cred *identity.Credentials, auth identity.AuthMode) *novaservice.Nova { 1805 var identityService identityservice.IdentityService 1806 if auth == identity.AuthKeyPair { 1807 identityService = identityservice.NewKeyPair() 1808 } else { 1809 identityService = identityservice.NewUserPass() 1810 } 1811 userInfo := identityService.AddUser(cred.User, cred.Secrets, cred.TenantName) 1812 if cred.TenantName == "" { 1813 panic("Openstack service double requires a tenant to be specified.") 1814 } 1815 novaService := novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, identityService) 1816 identityService.SetupHTTP(mux) 1817 novaService.SetupHTTP(mux) 1818 return novaService 1819 }