github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"net/url"
    13  	"strings"
    14  
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "launchpad.net/gocheck"
    17  	"launchpad.net/goose/client"
    18  	"launchpad.net/goose/identity"
    19  	"launchpad.net/goose/nova"
    20  	"launchpad.net/goose/testservices/hook"
    21  	"launchpad.net/goose/testservices/openstackservice"
    22  
    23  	"github.com/juju/juju/constraints"
    24  	"github.com/juju/juju/environs"
    25  	"github.com/juju/juju/environs/bootstrap"
    26  	"github.com/juju/juju/environs/config"
    27  	"github.com/juju/juju/environs/configstore"
    28  	"github.com/juju/juju/environs/imagemetadata"
    29  	"github.com/juju/juju/environs/jujutest"
    30  	"github.com/juju/juju/environs/simplestreams"
    31  	"github.com/juju/juju/environs/storage"
    32  	envtesting "github.com/juju/juju/environs/testing"
    33  	"github.com/juju/juju/environs/tools"
    34  	"github.com/juju/juju/instance"
    35  	"github.com/juju/juju/juju/arch"
    36  	"github.com/juju/juju/juju/testing"
    37  	"github.com/juju/juju/provider/openstack"
    38  	coretesting "github.com/juju/juju/testing"
    39  	"github.com/juju/juju/version"
    40  )
    41  
    42  type ProviderSuite struct {
    43  	restoreTimeouts func()
    44  }
    45  
    46  var _ = gc.Suite(&ProviderSuite{})
    47  var _ = gc.Suite(&localHTTPSServerSuite{})
    48  
    49  func (s *ProviderSuite) SetUpTest(c *gc.C) {
    50  	s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
    51  }
    52  
    53  func (s *ProviderSuite) TearDownTest(c *gc.C) {
    54  	s.restoreTimeouts()
    55  }
    56  
    57  // Register tests to run against a test Openstack instance (service doubles).
    58  func registerLocalTests() {
    59  	cred := &identity.Credentials{
    60  		User:       "fred",
    61  		Secrets:    "secret",
    62  		Region:     "some-region",
    63  		TenantName: "some tenant",
    64  	}
    65  	config := makeTestConfig(cred)
    66  	config["agent-version"] = version.Current.Number.String()
    67  	config["authorized-keys"] = "fakekey"
    68  	gc.Suite(&localLiveSuite{
    69  		LiveTests: LiveTests{
    70  			cred: cred,
    71  			LiveTests: jujutest.LiveTests{
    72  				TestConfig: config,
    73  			},
    74  		},
    75  	})
    76  	gc.Suite(&localServerSuite{
    77  		cred: cred,
    78  		Tests: jujutest.Tests{
    79  			TestConfig: config,
    80  		},
    81  	})
    82  }
    83  
    84  // localServer is used to spin up a local Openstack service double.
    85  type localServer struct {
    86  	Server          *httptest.Server
    87  	Mux             *http.ServeMux
    88  	oldHandler      http.Handler
    89  	Service         *openstackservice.Openstack
    90  	restoreTimeouts func()
    91  	UseTLS          bool
    92  }
    93  
    94  func (s *localServer) start(c *gc.C, cred *identity.Credentials) {
    95  	// Set up the HTTP server.
    96  	if s.UseTLS {
    97  		s.Server = httptest.NewTLSServer(nil)
    98  	} else {
    99  		s.Server = httptest.NewServer(nil)
   100  	}
   101  	c.Assert(s.Server, gc.NotNil)
   102  	s.oldHandler = s.Server.Config.Handler
   103  	s.Mux = http.NewServeMux()
   104  	s.Server.Config.Handler = s.Mux
   105  	cred.URL = s.Server.URL
   106  	c.Logf("Started service at: %v", s.Server.URL)
   107  	s.Service = openstackservice.New(cred, identity.AuthUserPass)
   108  	s.Service.SetupHTTP(s.Mux)
   109  	s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
   110  }
   111  
   112  func (s *localServer) stop() {
   113  	s.Mux = nil
   114  	s.Server.Config.Handler = s.oldHandler
   115  	s.Server.Close()
   116  	s.restoreTimeouts()
   117  }
   118  
   119  // localLiveSuite runs tests from LiveTests using an Openstack service double.
   120  type localLiveSuite struct {
   121  	coretesting.BaseSuite
   122  	LiveTests
   123  	srv localServer
   124  }
   125  
   126  func (s *localLiveSuite) SetUpSuite(c *gc.C) {
   127  	s.BaseSuite.SetUpSuite(c)
   128  	c.Logf("Running live tests using openstack service test double")
   129  	s.srv.start(c, s.cred)
   130  	s.LiveTests.SetUpSuite(c)
   131  	openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred)
   132  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   133  	s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() })
   134  }
   135  
   136  func (s *localLiveSuite) TearDownSuite(c *gc.C) {
   137  	openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env))
   138  	s.LiveTests.TearDownSuite(c)
   139  	s.srv.stop()
   140  	s.BaseSuite.TearDownSuite(c)
   141  }
   142  
   143  func (s *localLiveSuite) SetUpTest(c *gc.C) {
   144  	s.BaseSuite.SetUpTest(c)
   145  	s.LiveTests.SetUpTest(c)
   146  	s.PatchValue(&imagemetadata.DefaultBaseURL, "")
   147  }
   148  
   149  func (s *localLiveSuite) TearDownTest(c *gc.C) {
   150  	s.LiveTests.TearDownTest(c)
   151  	s.BaseSuite.TearDownTest(c)
   152  }
   153  
   154  // localServerSuite contains tests that run against an Openstack service double.
   155  // These tests can test things that would be unreasonably slow or expensive
   156  // to test on a live Openstack server. The service double is started and stopped for
   157  // each test.
   158  type localServerSuite struct {
   159  	coretesting.BaseSuite
   160  	jujutest.Tests
   161  	cred                 *identity.Credentials
   162  	srv                  localServer
   163  	toolsMetadataStorage storage.Storage
   164  	imageMetadataStorage storage.Storage
   165  }
   166  
   167  func (s *localServerSuite) SetUpSuite(c *gc.C) {
   168  	s.BaseSuite.SetUpSuite(c)
   169  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   170  	s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() })
   171  	c.Logf("Running local tests")
   172  }
   173  
   174  func (s *localServerSuite) SetUpTest(c *gc.C) {
   175  	s.BaseSuite.SetUpTest(c)
   176  	s.srv.start(c, s.cred)
   177  	cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
   178  	err := cl.Authenticate()
   179  	c.Assert(err, gc.IsNil)
   180  	containerURL, err := cl.MakeServiceURL("object-store", nil)
   181  	c.Assert(err, gc.IsNil)
   182  	s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{
   183  		"tools-metadata-url": containerURL + "/juju-dist-test/tools",
   184  		"image-metadata-url": containerURL + "/juju-dist-test",
   185  		"auth-url":           s.cred.URL,
   186  	})
   187  	s.Tests.SetUpTest(c)
   188  	// For testing, we create a storage instance to which is uploaded tools and image metadata.
   189  	env := s.Prepare(c)
   190  	s.toolsMetadataStorage = openstack.MetadataStorage(env)
   191  	// Put some fake metadata in place so that tests that are simply
   192  	// starting instances without any need to check if those instances
   193  	// are running can find the metadata.
   194  	envtesting.UploadFakeTools(c, s.toolsMetadataStorage)
   195  	s.imageMetadataStorage = openstack.ImageMetadataStorage(env)
   196  	openstack.UseTestImageData(s.imageMetadataStorage, s.cred)
   197  }
   198  
   199  func (s *localServerSuite) TearDownTest(c *gc.C) {
   200  	if s.imageMetadataStorage != nil {
   201  		openstack.RemoveTestImageData(s.imageMetadataStorage)
   202  	}
   203  	if s.toolsMetadataStorage != nil {
   204  		envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage)
   205  	}
   206  	s.Tests.TearDownTest(c)
   207  	s.srv.stop()
   208  	s.BaseSuite.TearDownTest(c)
   209  }
   210  
   211  // If the bootstrap node is configured to require a public IP address,
   212  // bootstrapping fails if an address cannot be allocated.
   213  func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) {
   214  	cleanup := s.srv.Service.Nova.RegisterControlPoint(
   215  		"addFloatingIP",
   216  		func(sc hook.ServiceControl, args ...interface{}) error {
   217  			return fmt.Errorf("failed on purpose")
   218  		},
   219  	)
   220  	defer cleanup()
   221  
   222  	// Create a config that matches s.TestConfig but with use-floating-ip set to true
   223  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   224  		"use-floating-ip": true,
   225  	}))
   226  	c.Assert(err, gc.IsNil)
   227  	env, err := environs.New(cfg)
   228  	c.Assert(err, gc.IsNil)
   229  	err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   230  	c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*")
   231  }
   232  
   233  // If the environment is configured not to require a public IP address for nodes,
   234  // bootstrapping and starting an instance should occur without any attempt to
   235  // allocate a public address.
   236  func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) {
   237  	cleanup := s.srv.Service.Nova.RegisterControlPoint(
   238  		"addFloatingIP",
   239  		func(sc hook.ServiceControl, args ...interface{}) error {
   240  			return fmt.Errorf("add floating IP should not have been called")
   241  		},
   242  	)
   243  	defer cleanup()
   244  	cleanup = s.srv.Service.Nova.RegisterControlPoint(
   245  		"addServerFloatingIP",
   246  		func(sc hook.ServiceControl, args ...interface{}) error {
   247  			return fmt.Errorf("add server floating IP should not have been called")
   248  		},
   249  	)
   250  	defer cleanup()
   251  
   252  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   253  		"use-floating-ip": false,
   254  	}))
   255  	c.Assert(err, gc.IsNil)
   256  	env, err := environs.Prepare(cfg, coretesting.Context(c), s.ConfigStore)
   257  	c.Assert(err, gc.IsNil)
   258  	err = bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   259  	c.Assert(err, gc.IsNil)
   260  	inst, _ := testing.AssertStartInstance(c, env, "100")
   261  	err = env.StopInstances(inst.Id())
   262  	c.Assert(err, gc.IsNil)
   263  }
   264  
   265  func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   266  	// Ensure amd64 tools are available, to ensure an amd64 image.
   267  	amd64Version := version.Current
   268  	amd64Version.Arch = arch.AMD64
   269  	for _, series := range bootstrap.ToolsLtsSeries {
   270  		amd64Version.Series = series
   271  		envtesting.AssertUploadFakeToolsVersions(c, s.toolsMetadataStorage, amd64Version)
   272  	}
   273  
   274  	env := s.Prepare(c)
   275  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   276  	c.Assert(err, gc.IsNil)
   277  	_, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024"))
   278  	c.Check(*hc.Arch, gc.Equals, "amd64")
   279  	c.Check(*hc.Mem, gc.Equals, uint64(2048))
   280  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   281  	c.Assert(hc.CpuPower, gc.IsNil)
   282  }
   283  
   284  func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) {
   285  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   286  		// A label that corresponds to a nova test service network
   287  		"network": "net",
   288  	}))
   289  	c.Assert(err, gc.IsNil)
   290  	env, err := environs.New(cfg)
   291  	c.Assert(err, gc.IsNil)
   292  	inst, _ := testing.AssertStartInstance(c, env, "100")
   293  	err = env.StopInstances(inst.Id())
   294  	c.Assert(err, gc.IsNil)
   295  }
   296  
   297  func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) {
   298  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   299  		// A label that has no related network in the nova test service
   300  		"network": "no-network-with-this-label",
   301  	}))
   302  	c.Assert(err, gc.IsNil)
   303  	env, err := environs.New(cfg)
   304  	c.Assert(err, gc.IsNil)
   305  	inst, _, _, err := testing.StartInstance(env, "100")
   306  	c.Check(inst, gc.IsNil)
   307  	c.Assert(err, gc.ErrorMatches, "No networks exist with label .*")
   308  }
   309  
   310  func (s *localServerSuite) TestStartInstanceNetworkUnknownId(c *gc.C) {
   311  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   312  		// A valid UUID but no related network in the nova test service
   313  		"network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
   314  	}))
   315  	c.Assert(err, gc.IsNil)
   316  	env, err := environs.New(cfg)
   317  	c.Assert(err, gc.IsNil)
   318  	inst, _, _, err := testing.StartInstance(env, "100")
   319  	c.Check(inst, gc.IsNil)
   320  	c.Assert(err, gc.ErrorMatches, "cannot run instance: (\\n|.)*"+
   321  		"caused by: "+
   322  		"request \\(.*/servers\\) returned unexpected status: "+
   323  		"404; error info: .*itemNotFound.*")
   324  }
   325  
   326  func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) {
   327  	novaClient := openstack.GetNovaClient(env)
   328  	groups, err := novaClient.ListSecurityGroups()
   329  	c.Assert(err, gc.IsNil)
   330  	for _, name := range expected {
   331  		found := false
   332  		for _, group := range groups {
   333  			if group.Name == name {
   334  				found = true
   335  				break
   336  			}
   337  		}
   338  		if !found {
   339  			c.Errorf("expected security group %q not found", name)
   340  		}
   341  	}
   342  	for _, group := range groups {
   343  		found := false
   344  		for _, name := range expected {
   345  			if group.Name == name {
   346  				found = true
   347  				break
   348  			}
   349  		}
   350  		if !found {
   351  			c.Errorf("existing security group %q is not expected", group.Name)
   352  		}
   353  	}
   354  }
   355  
   356  func (s *localServerSuite) TestStopInstance(c *gc.C) {
   357  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   358  		"firewall-mode": "instance"}))
   359  	c.Assert(err, gc.IsNil)
   360  	env, err := environs.New(cfg)
   361  	c.Assert(err, gc.IsNil)
   362  	instanceName := "100"
   363  	inst, _ := testing.AssertStartInstance(c, env, instanceName)
   364  	// Openstack now has three security groups for the server, the default
   365  	// group, one group for the entire environment, and another for the
   366  	// new instance.
   367  	assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)})
   368  	err = env.StopInstances(inst.Id())
   369  	c.Assert(err, gc.IsNil)
   370  	// The security group for this instance is now removed.
   371  	assertSecurityGroups(c, env, []string{"default", fmt.Sprintf("juju-%v", env.Name())})
   372  }
   373  
   374  // Due to bug #1300755 it can happen that the security group intended for
   375  // an instance is also used as the common security group of another
   376  // environment. If this is the case, the attempt to delete the instance's
   377  // security group fails but StopInstance succeeds.
   378  func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) {
   379  	// Force an error when a security group is deleted.
   380  	cleanup := s.srv.Service.Nova.RegisterControlPoint(
   381  		"removeSecurityGroup",
   382  		func(sc hook.ServiceControl, args ...interface{}) error {
   383  			return fmt.Errorf("failed on purpose")
   384  		},
   385  	)
   386  	defer cleanup()
   387  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   388  		"firewall-mode": "instance"}))
   389  	c.Assert(err, gc.IsNil)
   390  	env, err := environs.New(cfg)
   391  	c.Assert(err, gc.IsNil)
   392  	instanceName := "100"
   393  	inst, _ := testing.AssertStartInstance(c, env, instanceName)
   394  	allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}
   395  	assertSecurityGroups(c, env, allSecurityGroups)
   396  	err = env.StopInstances(inst.Id())
   397  	c.Assert(err, gc.IsNil)
   398  	assertSecurityGroups(c, env, allSecurityGroups)
   399  }
   400  
   401  func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) {
   402  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   403  		"firewall-mode": "instance"}))
   404  	c.Assert(err, gc.IsNil)
   405  	env, err := environs.New(cfg)
   406  	c.Assert(err, gc.IsNil)
   407  	instanceName := "100"
   408  	testing.AssertStartInstance(c, env, instanceName)
   409  	allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-%v", env.Name(), instanceName)}
   410  	assertSecurityGroups(c, env, allSecurityGroups)
   411  	err = env.Destroy()
   412  	c.Check(err, gc.IsNil)
   413  	assertSecurityGroups(c, env, []string{"default"})
   414  }
   415  
   416  func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) {
   417  	cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(coretesting.Attrs{
   418  		"firewall-mode": "global"}))
   419  	c.Assert(err, gc.IsNil)
   420  	env, err := environs.New(cfg)
   421  	c.Assert(err, gc.IsNil)
   422  	instanceName := "100"
   423  	testing.AssertStartInstance(c, env, instanceName)
   424  	allSecurityGroups := []string{"default", fmt.Sprintf("juju-%v", env.Name()), fmt.Sprintf("juju-%v-global", env.Name())}
   425  	assertSecurityGroups(c, env, allSecurityGroups)
   426  	err = env.Destroy()
   427  	c.Check(err, gc.IsNil)
   428  	assertSecurityGroups(c, env, []string{"default"})
   429  }
   430  
   431  var instanceGathering = []struct {
   432  	ids []instance.Id
   433  	err error
   434  }{
   435  	{ids: []instance.Id{"id0"}},
   436  	{ids: []instance.Id{"id0", "id0"}},
   437  	{ids: []instance.Id{"id0", "id1"}},
   438  	{ids: []instance.Id{"id1", "id0"}},
   439  	{ids: []instance.Id{"id1", "id0", "id1"}},
   440  	{
   441  		ids: []instance.Id{""},
   442  		err: environs.ErrNoInstances,
   443  	},
   444  	{
   445  		ids: []instance.Id{"", ""},
   446  		err: environs.ErrNoInstances,
   447  	},
   448  	{
   449  		ids: []instance.Id{"", "", ""},
   450  		err: environs.ErrNoInstances,
   451  	},
   452  	{
   453  		ids: []instance.Id{"id0", ""},
   454  		err: environs.ErrPartialInstances,
   455  	},
   456  	{
   457  		ids: []instance.Id{"", "id1"},
   458  		err: environs.ErrPartialInstances,
   459  	},
   460  	{
   461  		ids: []instance.Id{"id0", "id1", ""},
   462  		err: environs.ErrPartialInstances,
   463  	},
   464  	{
   465  		ids: []instance.Id{"id0", "", "id0"},
   466  		err: environs.ErrPartialInstances,
   467  	},
   468  	{
   469  		ids: []instance.Id{"id0", "id0", ""},
   470  		err: environs.ErrPartialInstances,
   471  	},
   472  	{
   473  		ids: []instance.Id{"", "id0", "id1"},
   474  		err: environs.ErrPartialInstances,
   475  	},
   476  }
   477  
   478  func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
   479  	env := s.Prepare(c)
   480  	// goose's test service always returns ACTIVE state.
   481  	inst, _ := testing.AssertStartInstance(c, env, "100")
   482  	c.Assert(inst.Status(), gc.Equals, nova.StatusActive)
   483  	err := env.StopInstances(inst.Id())
   484  	c.Assert(err, gc.IsNil)
   485  }
   486  
   487  func (s *localServerSuite) TestInstancesGathering(c *gc.C) {
   488  	env := s.Prepare(c)
   489  	inst0, _ := testing.AssertStartInstance(c, env, "100")
   490  	id0 := inst0.Id()
   491  	inst1, _ := testing.AssertStartInstance(c, env, "101")
   492  	id1 := inst1.Id()
   493  	defer func() {
   494  		err := env.StopInstances(inst0.Id(), inst1.Id())
   495  		c.Assert(err, gc.IsNil)
   496  	}()
   497  
   498  	for i, test := range instanceGathering {
   499  		c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err)
   500  		ids := make([]instance.Id, len(test.ids))
   501  		for j, id := range test.ids {
   502  			switch id {
   503  			case "id0":
   504  				ids[j] = id0
   505  			case "id1":
   506  				ids[j] = id1
   507  			}
   508  		}
   509  		insts, err := env.Instances(ids)
   510  		c.Assert(err, gc.Equals, test.err)
   511  		if err == environs.ErrNoInstances {
   512  			c.Assert(insts, gc.HasLen, 0)
   513  		} else {
   514  			c.Assert(insts, gc.HasLen, len(test.ids))
   515  		}
   516  		for j, inst := range insts {
   517  			if ids[j] != "" {
   518  				c.Assert(inst.Id(), gc.Equals, ids[j])
   519  			} else {
   520  				c.Assert(inst, gc.IsNil)
   521  			}
   522  		}
   523  	}
   524  }
   525  
   526  func (s *localServerSuite) TestCollectInstances(c *gc.C) {
   527  	env := s.Prepare(c)
   528  	cleanup := s.srv.Service.Nova.RegisterControlPoint(
   529  		"addServer",
   530  		func(sc hook.ServiceControl, args ...interface{}) error {
   531  			details := args[0].(*nova.ServerDetail)
   532  			details.Status = "BUILD(networking)"
   533  			return nil
   534  		},
   535  	)
   536  	defer cleanup()
   537  	stateInst, _ := testing.AssertStartInstance(c, env, "100")
   538  	defer func() {
   539  		err := env.StopInstances(stateInst.Id())
   540  		c.Assert(err, gc.IsNil)
   541  	}()
   542  	found := make(map[instance.Id]instance.Instance)
   543  	missing := []instance.Id{stateInst.Id()}
   544  
   545  	resultMissing := openstack.CollectInstances(env, missing, found)
   546  
   547  	c.Assert(resultMissing, gc.DeepEquals, missing)
   548  }
   549  
   550  func (s *localServerSuite) TestInstancesBuildSpawning(c *gc.C) {
   551  	env := s.Prepare(c)
   552  	// HP servers are available once they are BUILD(spawning).
   553  	cleanup := s.srv.Service.Nova.RegisterControlPoint(
   554  		"addServer",
   555  		func(sc hook.ServiceControl, args ...interface{}) error {
   556  			details := args[0].(*nova.ServerDetail)
   557  			details.Status = nova.StatusBuildSpawning
   558  			return nil
   559  		},
   560  	)
   561  	defer cleanup()
   562  	stateInst, _ := testing.AssertStartInstance(c, env, "100")
   563  	defer func() {
   564  		err := env.StopInstances(stateInst.Id())
   565  		c.Assert(err, gc.IsNil)
   566  	}()
   567  
   568  	instances, err := env.Instances([]instance.Id{stateInst.Id()})
   569  
   570  	c.Assert(err, gc.IsNil)
   571  	c.Assert(instances, gc.HasLen, 1)
   572  	c.Assert(instances[0].Status(), gc.Equals, nova.StatusBuildSpawning)
   573  }
   574  
   575  // TODO (wallyworld) - this test was copied from the ec2 provider.
   576  // It should be moved to environs.jujutests.Tests.
   577  func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
   578  	env := s.Prepare(c)
   579  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
   580  	c.Assert(err, gc.IsNil)
   581  
   582  	// check that the state holds the id of the bootstrap machine.
   583  	stateData, err := bootstrap.LoadState(env.Storage())
   584  	c.Assert(err, gc.IsNil)
   585  	c.Assert(stateData.StateInstances, gc.HasLen, 1)
   586  
   587  	insts, err := env.AllInstances()
   588  	c.Assert(err, gc.IsNil)
   589  	c.Assert(insts, gc.HasLen, 1)
   590  	c.Check(insts[0].Id(), gc.Equals, stateData.StateInstances[0])
   591  
   592  	addresses, err := insts[0].Addresses()
   593  	c.Assert(err, gc.IsNil)
   594  	c.Assert(addresses, gc.Not(gc.HasLen), 0)
   595  
   596  	// TODO(wallyworld) - 2013-03-01 bug=1137005
   597  	// The nova test double needs to be updated to support retrieving instance userData.
   598  	// Until then, we can't check the cloud init script was generated correctly.
   599  	// When we can, we should also check cloudinit for a non-manager node (as in the
   600  	// ec2 tests).
   601  }
   602  
   603  func (s *localServerSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) {
   604  	// Create a config that matches s.TestConfig but with the specified stream.
   605  	envAttrs := s.TestConfig
   606  	if stream != "" {
   607  		envAttrs = envAttrs.Merge(coretesting.Attrs{"image-stream": stream})
   608  	}
   609  	cfg, err := config.New(config.NoDefaults, envAttrs)
   610  	c.Assert(err, gc.IsNil)
   611  	env, err := environs.New(cfg)
   612  	c.Assert(err, gc.IsNil)
   613  	sources, err := imagemetadata.GetMetadataSources(env)
   614  	c.Assert(err, gc.IsNil)
   615  	c.Assert(sources, gc.HasLen, 4)
   616  	var urls = make([]string, len(sources))
   617  	for i, source := range sources {
   618  		url, err := source.URL("")
   619  		c.Assert(err, gc.IsNil)
   620  		urls[i] = url
   621  	}
   622  	// The image-metadata-url ends with "/juju-dist-test/".
   623  	c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue)
   624  	// The control bucket URL contains the bucket name.
   625  	c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/images"), jc.IsTrue)
   626  	// The product-streams URL ends with "/imagemetadata".
   627  	c.Check(strings.HasSuffix(urls[2], "/imagemetadata/"), jc.IsTrue)
   628  	c.Assert(urls[3], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath))
   629  }
   630  
   631  func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) {
   632  	s.assertGetImageMetadataSources(c, "", "releases")
   633  	s.assertGetImageMetadataSources(c, "released", "releases")
   634  	s.assertGetImageMetadataSources(c, "daily", "daily")
   635  }
   636  
   637  func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
   638  	env := s.Open(c)
   639  	sources, err := tools.GetMetadataSources(env)
   640  	c.Assert(err, gc.IsNil)
   641  	c.Assert(sources, gc.HasLen, 3)
   642  	var urls = make([]string, len(sources))
   643  	for i, source := range sources {
   644  		url, err := source.URL("")
   645  		c.Assert(err, gc.IsNil)
   646  		urls[i] = url
   647  	}
   648  	// The tools-metadata-url ends with "/juju-dist-test/tools/".
   649  	c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue)
   650  	// The control bucket URL contains the bucket name.
   651  	c.Check(strings.Contains(urls[1], openstack.ControlBucketName(env)+"/tools"), jc.IsTrue)
   652  	// Check that the URL from keystone parses.
   653  	_, err = url.Parse(urls[2])
   654  	c.Assert(err, gc.IsNil)
   655  }
   656  
   657  func (s *localServerSuite) TestSupportedArchitectures(c *gc.C) {
   658  	env := s.Open(c)
   659  	a, err := env.SupportedArchitectures()
   660  	c.Assert(err, gc.IsNil)
   661  	c.Assert(a, jc.SameContents, []string{"amd64", "i386", "ppc64"})
   662  }
   663  
   664  func (s *localServerSuite) TestSupportNetworks(c *gc.C) {
   665  	env := s.Open(c)
   666  	c.Assert(env.SupportNetworks(), jc.IsFalse)
   667  }
   668  
   669  func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
   670  	// Prevent falling over to the public datasource.
   671  	s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "")
   672  
   673  	env := s.Open(c)
   674  
   675  	// An error occurs if no suitable image is found.
   676  	_, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G")
   677  	c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`)
   678  }
   679  
   680  func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
   681  	env := s.Open(c)
   682  	validator, err := env.ConstraintsValidator()
   683  	c.Assert(err, gc.IsNil)
   684  	cons := constraints.MustParse("arch=amd64 cpu-power=10")
   685  	unsupported, err := validator.Validate(cons)
   686  	c.Assert(err, gc.IsNil)
   687  	c.Assert(unsupported, jc.SameContents, []string{"cpu-power"})
   688  }
   689  
   690  func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
   691  	env := s.Open(c)
   692  	validator, err := env.ConstraintsValidator()
   693  	c.Assert(err, gc.IsNil)
   694  	cons := constraints.MustParse("arch=arm64")
   695  	_, err = validator.Validate(cons)
   696  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=arm64\nvalid values are:.*")
   697  	cons = constraints.MustParse("instance-type=foo")
   698  	_, err = validator.Validate(cons)
   699  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
   700  }
   701  
   702  func (s *localServerSuite) TestConstraintsMerge(c *gc.C) {
   703  	env := s.Open(c)
   704  	validator, err := env.ConstraintsValidator()
   705  	c.Assert(err, gc.IsNil)
   706  	consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G")
   707  	consB := constraints.MustParse("instance-type=m1.small")
   708  	cons, err := validator.Merge(consA, consB)
   709  	c.Assert(err, gc.IsNil)
   710  	c.Assert(cons, gc.DeepEquals, constraints.MustParse("instance-type=m1.small"))
   711  }
   712  
   713  func (s *localServerSuite) TestFindImageInstanceConstraint(c *gc.C) {
   714  	// Prevent falling over to the public datasource.
   715  	s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "")
   716  
   717  	env := s.Open(c)
   718  	spec, err := openstack.FindInstanceSpec(env, "precise", "amd64", "instance-type=m1.tiny")
   719  	c.Assert(err, gc.IsNil)
   720  	c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny")
   721  }
   722  
   723  func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) {
   724  	// Prevent falling over to the public datasource.
   725  	s.BaseSuite.PatchValue(&imagemetadata.DefaultBaseURL, "")
   726  
   727  	env := s.Open(c)
   728  	_, err := openstack.FindInstanceSpec(env, "precise", "amd64", "instance-type=m1.large")
   729  	c.Assert(err, gc.ErrorMatches, `invalid instance type "m1.large"`)
   730  }
   731  
   732  func (s *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
   733  	env := s.Open(c)
   734  	cons := constraints.MustParse("instance-type=m1.small")
   735  	placement := ""
   736  	err := env.PrecheckInstance("precise", cons, placement)
   737  	c.Assert(err, gc.IsNil)
   738  }
   739  
   740  func (s *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
   741  	env := s.Open(c)
   742  	cons := constraints.MustParse("instance-type=m1.large")
   743  	placement := ""
   744  	err := env.PrecheckInstance("precise", cons, placement)
   745  	c.Assert(err, gc.ErrorMatches, `invalid Openstack flavour "m1.large" specified`)
   746  }
   747  
   748  func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
   749  	env := s.Open(c)
   750  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region")
   751  	c.Assert(err, gc.IsNil)
   752  	params.Sources, err = imagemetadata.GetMetadataSources(env)
   753  	c.Assert(err, gc.IsNil)
   754  	params.Series = "raring"
   755  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
   756  	c.Assert(err, gc.IsNil)
   757  	c.Assert(image_ids, jc.SameContents, []string{"id-y"})
   758  }
   759  
   760  func (s *localServerSuite) TestRemoveAll(c *gc.C) {
   761  	env := s.Prepare(c)
   762  	stor := env.Storage()
   763  	for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") {
   764  		content := []byte{a}
   765  		name := string(content)
   766  		err := stor.Put(name, bytes.NewBuffer(content),
   767  			int64(len(content)))
   768  		c.Assert(err, gc.IsNil)
   769  	}
   770  	reader, err := storage.Get(stor, "a")
   771  	c.Assert(err, gc.IsNil)
   772  	allContent, err := ioutil.ReadAll(reader)
   773  	c.Assert(err, gc.IsNil)
   774  	c.Assert(string(allContent), gc.Equals, "a")
   775  	err = stor.RemoveAll()
   776  	c.Assert(err, gc.IsNil)
   777  	_, err = storage.Get(stor, "a")
   778  	c.Assert(err, gc.NotNil)
   779  }
   780  
   781  func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) {
   782  	env := s.Prepare(c)
   783  	stor := env.Storage()
   784  	// 6*26 = 156 items
   785  	for _, a := range []byte("abcdef") {
   786  		for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") {
   787  			content := []byte{a, b}
   788  			name := string(content)
   789  			err := stor.Put(name, bytes.NewBuffer(content),
   790  				int64(len(content)))
   791  			c.Assert(err, gc.IsNil)
   792  		}
   793  	}
   794  	reader, err := storage.Get(stor, "ab")
   795  	c.Assert(err, gc.IsNil)
   796  	allContent, err := ioutil.ReadAll(reader)
   797  	c.Assert(err, gc.IsNil)
   798  	c.Assert(string(allContent), gc.Equals, "ab")
   799  	err = stor.RemoveAll()
   800  	c.Assert(err, gc.IsNil)
   801  	_, err = storage.Get(stor, "ab")
   802  	c.Assert(err, gc.NotNil)
   803  }
   804  
   805  // TestEnsureGroup checks that when creating a duplicate security group, the existing group is
   806  // returned and the existing rules have been left as is.
   807  func (s *localServerSuite) TestEnsureGroup(c *gc.C) {
   808  	env := s.Prepare(c)
   809  	rule := []nova.RuleInfo{
   810  		{
   811  			IPProtocol: "tcp",
   812  			FromPort:   22,
   813  			ToPort:     22,
   814  		},
   815  	}
   816  
   817  	assertRule := func(group nova.SecurityGroup) {
   818  		c.Check(len(group.Rules), gc.Equals, 1)
   819  		c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp")
   820  		c.Check(*group.Rules[0].FromPort, gc.Equals, 22)
   821  		c.Check(*group.Rules[0].ToPort, gc.Equals, 22)
   822  	}
   823  
   824  	group, err := openstack.EnsureGroup(env, "test group", rule)
   825  	c.Assert(err, gc.IsNil)
   826  	c.Assert(group.Name, gc.Equals, "test group")
   827  	assertRule(group)
   828  	id := group.Id
   829  	// Do it again and check that the existing group is returned.
   830  	anotherRule := []nova.RuleInfo{
   831  		{
   832  			IPProtocol: "tcp",
   833  			FromPort:   1,
   834  			ToPort:     65535,
   835  		},
   836  	}
   837  	group, err = openstack.EnsureGroup(env, "test group", anotherRule)
   838  	c.Assert(err, gc.IsNil)
   839  	c.Check(group.Id, gc.Equals, id)
   840  	c.Assert(group.Name, gc.Equals, "test group")
   841  	assertRule(group)
   842  }
   843  
   844  // localHTTPSServerSuite contains tests that run against an Openstack service
   845  // double connected on an HTTPS port with a self-signed certificate. This
   846  // service is set up and torn down for every test.  This should only test
   847  // things that depend on the HTTPS connection, all other functional tests on a
   848  // local connection should be in localServerSuite
   849  type localHTTPSServerSuite struct {
   850  	coretesting.BaseSuite
   851  	attrs map[string]interface{}
   852  	cred  *identity.Credentials
   853  	srv   localServer
   854  	env   environs.Environ
   855  }
   856  
   857  func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} {
   858  	attrs := makeTestConfig(s.cred)
   859  	attrs["agent-version"] = version.Current.Number.String()
   860  	attrs["authorized-keys"] = "fakekey"
   861  	// In order to set up and tear down the environment properly, we must
   862  	// disable hostname verification
   863  	attrs["ssl-hostname-verification"] = false
   864  	attrs["auth-url"] = s.cred.URL
   865  	// Now connect and set up test-local tools and image-metadata URLs
   866  	cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
   867  	err := cl.Authenticate()
   868  	c.Assert(err, gc.IsNil)
   869  	containerURL, err := cl.MakeServiceURL("object-store", nil)
   870  	c.Assert(err, gc.IsNil)
   871  	c.Check(containerURL[:8], gc.Equals, "https://")
   872  	attrs["tools-metadata-url"] = containerURL + "/juju-dist-test/tools"
   873  	c.Logf("Set tools-metadata-url=%q", attrs["tools-metadata-url"])
   874  	attrs["image-metadata-url"] = containerURL + "/juju-dist-test"
   875  	c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"])
   876  	return attrs
   877  }
   878  
   879  func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) {
   880  	s.BaseSuite.SetUpTest(c)
   881  	s.srv.UseTLS = true
   882  	cred := &identity.Credentials{
   883  		User:       "fred",
   884  		Secrets:    "secret",
   885  		Region:     "some-region",
   886  		TenantName: "some tenant",
   887  	}
   888  	// Note: start() will change cred.URL to point to s.srv.Server.URL
   889  	s.srv.start(c, cred)
   890  	s.cred = cred
   891  	attrs := s.createConfigAttrs(c)
   892  	c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://")
   893  	cfg, err := config.New(config.NoDefaults, attrs)
   894  	c.Assert(err, gc.IsNil)
   895  	s.env, err = environs.Prepare(cfg, coretesting.Context(c), configstore.NewMem())
   896  	c.Assert(err, gc.IsNil)
   897  	s.attrs = s.env.Config().AllAttrs()
   898  }
   899  
   900  func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) {
   901  	if s.env != nil {
   902  		err := s.env.Destroy()
   903  		c.Check(err, gc.IsNil)
   904  		s.env = nil
   905  	}
   906  	s.srv.stop()
   907  	s.BaseSuite.TearDownTest(c)
   908  }
   909  
   910  func (s *localHTTPSServerSuite) TestCanUploadTools(c *gc.C) {
   911  	envtesting.UploadFakeTools(c, s.env.Storage())
   912  }
   913  
   914  func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) {
   915  	// If you don't have ssl-hostname-verification set to false, then we
   916  	// fail to connect to the environment. Copy the attrs used by SetUp and
   917  	// force hostname verification.
   918  	newattrs := make(map[string]interface{}, len(s.attrs))
   919  	for k, v := range s.attrs {
   920  		newattrs[k] = v
   921  	}
   922  	newattrs["ssl-hostname-verification"] = true
   923  	env, err := environs.NewFromAttrs(newattrs)
   924  	c.Assert(err, gc.IsNil)
   925  	err = env.Storage().Put("test-name", strings.NewReader("content"), 7)
   926  	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
   927  	// However, it works just fine if you use the one with the credentials set
   928  	err = s.env.Storage().Put("test-name", strings.NewReader("content"), 7)
   929  	c.Assert(err, gc.IsNil)
   930  	_, err = env.Storage().Get("test-name")
   931  	c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
   932  	reader, err := s.env.Storage().Get("test-name")
   933  	c.Assert(err, gc.IsNil)
   934  	contents, err := ioutil.ReadAll(reader)
   935  	c.Assert(string(contents), gc.Equals, "content")
   936  }
   937  
   938  func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) {
   939  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   940  	defer restoreFinishBootstrap()
   941  
   942  	// For testing, we create a storage instance to which is uploaded tools and image metadata.
   943  	metadataStorage := openstack.MetadataStorage(s.env)
   944  	url, err := metadataStorage.URL("")
   945  	c.Assert(err, gc.IsNil)
   946  	c.Logf("Generating fake tools for: %v", url)
   947  	envtesting.UploadFakeTools(c, metadataStorage)
   948  	defer envtesting.RemoveFakeTools(c, metadataStorage)
   949  	openstack.UseTestImageData(metadataStorage, s.cred)
   950  	defer openstack.RemoveTestImageData(metadataStorage)
   951  
   952  	err = bootstrap.Bootstrap(coretesting.Context(c), s.env, environs.BootstrapParams{})
   953  	c.Assert(err, gc.IsNil)
   954  }
   955  
   956  func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) {
   957  	// Setup a custom URL for image metadata
   958  	customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata")
   959  	customURL, err := customStorage.URL("")
   960  	c.Assert(err, gc.IsNil)
   961  	c.Check(customURL[:8], gc.Equals, "https://")
   962  
   963  	config, err := s.env.Config().Apply(
   964  		map[string]interface{}{"image-metadata-url": customURL},
   965  	)
   966  	c.Assert(err, gc.IsNil)
   967  	err = s.env.SetConfig(config)
   968  	c.Assert(err, gc.IsNil)
   969  	sources, err := imagemetadata.GetMetadataSources(s.env)
   970  	c.Assert(err, gc.IsNil)
   971  	c.Assert(sources, gc.HasLen, 4)
   972  
   973  	// Make sure there is something to download from each location
   974  	private := "private-content"
   975  	err = s.env.Storage().Put("images/"+private, bytes.NewBufferString(private), int64(len(private)))
   976  	c.Assert(err, gc.IsNil)
   977  
   978  	metadata := "metadata-content"
   979  	metadataStorage := openstack.ImageMetadataStorage(s.env)
   980  	err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata)))
   981  	c.Assert(err, gc.IsNil)
   982  
   983  	custom := "custom-content"
   984  	err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
   985  	c.Assert(err, gc.IsNil)
   986  
   987  	// Read from the Config entry's image-metadata-url
   988  	contentReader, url, err := sources[0].Fetch(custom)
   989  	c.Assert(err, gc.IsNil)
   990  	defer contentReader.Close()
   991  	content, err := ioutil.ReadAll(contentReader)
   992  	c.Assert(err, gc.IsNil)
   993  	c.Assert(string(content), gc.Equals, custom)
   994  	c.Check(url[:8], gc.Equals, "https://")
   995  
   996  	// Read from the private bucket
   997  	contentReader, url, err = sources[1].Fetch(private)
   998  	c.Assert(err, gc.IsNil)
   999  	defer contentReader.Close()
  1000  	content, err = ioutil.ReadAll(contentReader)
  1001  	c.Assert(err, gc.IsNil)
  1002  	c.Check(string(content), gc.Equals, private)
  1003  	c.Check(url[:8], gc.Equals, "https://")
  1004  
  1005  	// Check the entry we got from keystone
  1006  	contentReader, url, err = sources[2].Fetch(metadata)
  1007  	c.Assert(err, gc.IsNil)
  1008  	defer contentReader.Close()
  1009  	content, err = ioutil.ReadAll(contentReader)
  1010  	c.Assert(err, gc.IsNil)
  1011  	c.Assert(string(content), gc.Equals, metadata)
  1012  	c.Check(url[:8], gc.Equals, "https://")
  1013  	// Verify that we are pointing at exactly where metadataStorage thinks we are
  1014  	metaURL, err := metadataStorage.URL(metadata)
  1015  	c.Assert(err, gc.IsNil)
  1016  	c.Check(url, gc.Equals, metaURL)
  1017  
  1018  }
  1019  
  1020  func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) {
  1021  	// Setup a custom URL for image metadata
  1022  	customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata")
  1023  	customURL, err := customStorage.URL("")
  1024  	c.Assert(err, gc.IsNil)
  1025  	c.Check(customURL[:8], gc.Equals, "https://")
  1026  
  1027  	config, err := s.env.Config().Apply(
  1028  		map[string]interface{}{"tools-metadata-url": customURL},
  1029  	)
  1030  	c.Assert(err, gc.IsNil)
  1031  	err = s.env.SetConfig(config)
  1032  	c.Assert(err, gc.IsNil)
  1033  	sources, err := tools.GetMetadataSources(s.env)
  1034  	c.Assert(err, gc.IsNil)
  1035  	c.Assert(sources, gc.HasLen, 4)
  1036  
  1037  	// Make sure there is something to download from each location
  1038  	private := "private-tools-content"
  1039  	// The Private data storage always tacks on "tools/" to the URL stream,
  1040  	// so add it in here
  1041  	err = s.env.Storage().Put("tools/"+private, bytes.NewBufferString(private), int64(len(private)))
  1042  	c.Assert(err, gc.IsNil)
  1043  
  1044  	keystone := "keystone-tools-content"
  1045  	// The keystone entry just points at the root of the Swift storage, and
  1046  	// we have to create a container to upload any data. So we just point
  1047  	// into a subdirectory for the data we are downloading
  1048  	keystoneContainer := "tools-test"
  1049  	keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test")
  1050  	err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone)))
  1051  	c.Assert(err, gc.IsNil)
  1052  
  1053  	custom := "custom-tools-content"
  1054  	err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
  1055  	c.Assert(err, gc.IsNil)
  1056  
  1057  	// Read from the Config entry's tools-metadata-url
  1058  	contentReader, url, err := sources[0].Fetch(custom)
  1059  	c.Assert(err, gc.IsNil)
  1060  	defer contentReader.Close()
  1061  	content, err := ioutil.ReadAll(contentReader)
  1062  	c.Assert(err, gc.IsNil)
  1063  	c.Assert(string(content), gc.Equals, custom)
  1064  	c.Check(url[:8], gc.Equals, "https://")
  1065  
  1066  	// Read from the private bucket
  1067  	contentReader, url, err = sources[1].Fetch(private)
  1068  	c.Assert(err, gc.IsNil)
  1069  	defer contentReader.Close()
  1070  	content, err = ioutil.ReadAll(contentReader)
  1071  	c.Assert(err, gc.IsNil)
  1072  	c.Check(string(content), gc.Equals, private)
  1073  	//c.Check(url[:8], gc.Equals, "https://")
  1074  	c.Check(strings.HasSuffix(url, "tools/"+private), jc.IsTrue)
  1075  
  1076  	// Check the entry we got from keystone
  1077  	// Now fetch the data, and verify the contents.
  1078  	contentReader, url, err = sources[2].Fetch(keystoneContainer + "/" + keystone)
  1079  	c.Assert(err, gc.IsNil)
  1080  	defer contentReader.Close()
  1081  	content, err = ioutil.ReadAll(contentReader)
  1082  	c.Assert(err, gc.IsNil)
  1083  	c.Assert(string(content), gc.Equals, keystone)
  1084  	c.Check(url[:8], gc.Equals, "https://")
  1085  	keystoneURL, err := keystoneStorage.URL(keystone)
  1086  	c.Assert(err, gc.IsNil)
  1087  	c.Check(url, gc.Equals, keystoneURL)
  1088  
  1089  	// We *don't* test Fetch for sources[3] because it points to
  1090  	// streams.canonical.com
  1091  }
  1092  
  1093  func (s *localServerSuite) TestAllInstancesIgnoresOtherMachines(c *gc.C) {
  1094  	env := s.Prepare(c)
  1095  	err := bootstrap.Bootstrap(coretesting.Context(c), env, environs.BootstrapParams{})
  1096  	c.Assert(err, gc.IsNil)
  1097  
  1098  	// Check that we see 1 instance in the environment
  1099  	insts, err := env.AllInstances()
  1100  	c.Assert(err, gc.IsNil)
  1101  	c.Check(insts, gc.HasLen, 1)
  1102  
  1103  	// Now start a machine 'manually' in the same account, with a similar
  1104  	// but not matching name, and ensure it isn't seen by AllInstances
  1105  	// See bug #1257481, for how similar names were causing them to get
  1106  	// listed (and thus destroyed) at the wrong time
  1107  	existingEnvName := s.TestConfig["name"]
  1108  	newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingEnvName)
  1109  
  1110  	// We grab the Nova client directly from the env, just to save time
  1111  	// looking all the stuff up
  1112  	novaClient := openstack.GetNovaClient(env)
  1113  	entity, err := novaClient.RunServer(nova.RunServerOpts{
  1114  		Name:     newMachineName,
  1115  		FlavorId: "1", // test service has 1,2,3 for flavor ids
  1116  		ImageId:  "1", // UseTestImageData sets up images 1 and 2
  1117  	})
  1118  	c.Assert(err, gc.IsNil)
  1119  	c.Assert(entity, gc.NotNil)
  1120  
  1121  	// List all servers with no filter, we should see both instances
  1122  	servers, err := novaClient.ListServersDetail(nova.NewFilter())
  1123  	c.Assert(err, gc.IsNil)
  1124  	c.Assert(servers, gc.HasLen, 2)
  1125  
  1126  	insts, err = env.AllInstances()
  1127  	c.Assert(err, gc.IsNil)
  1128  	c.Check(insts, gc.HasLen, 1)
  1129  }
  1130  
  1131  func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) {
  1132  	env := s.Prepare(c)
  1133  	var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
  1134  	networkId, err := openstack.ResolveNetwork(env, sampleUUID)
  1135  	c.Assert(err, gc.IsNil)
  1136  	c.Assert(networkId, gc.Equals, sampleUUID)
  1137  }
  1138  
  1139  func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) {
  1140  	env := s.Prepare(c)
  1141  	// For now this test has to cheat and use knowledge of goose internals
  1142  	var networkLabel = "net"
  1143  	var expectNetworkId = "1"
  1144  	networkId, err := openstack.ResolveNetwork(env, networkLabel)
  1145  	c.Assert(err, gc.IsNil)
  1146  	c.Assert(networkId, gc.Equals, expectNetworkId)
  1147  }
  1148  
  1149  func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) {
  1150  	env := s.Prepare(c)
  1151  	var notPresentNetwork = "no-network-with-this-label"
  1152  	networkId, err := openstack.ResolveNetwork(env, notPresentNetwork)
  1153  	c.Check(networkId, gc.Equals, "")
  1154  	c.Assert(err, gc.ErrorMatches, `No networks exist with label "no-network-with-this-label"`)
  1155  }
  1156  
  1157  // TODO(gz): TestResolveNetworkMultipleMatching when can inject new networks