github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/provider/openstack/local_test.go (about)

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