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