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