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