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