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