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