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