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  }