github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/joyent/local_test.go (about)

     1  // Copyright 2013 Joyent Inc.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package joyent_test
     5  
     6  import (
     7  	"net/http"
     8  	"net/http/httptest"
     9  
    10  	lc "github.com/joyent/gosdc/localservices/cloudapi"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/arch"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/cloud"
    16  	"github.com/juju/juju/constraints"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/environs/bootstrap"
    19  	"github.com/juju/juju/environs/imagemetadata"
    20  	imagetesting "github.com/juju/juju/environs/imagemetadata/testing"
    21  	"github.com/juju/juju/environs/jujutest"
    22  	"github.com/juju/juju/environs/simplestreams"
    23  	sstesting "github.com/juju/juju/environs/simplestreams/testing"
    24  	envtesting "github.com/juju/juju/environs/testing"
    25  	"github.com/juju/juju/environs/tools"
    26  	"github.com/juju/juju/instance"
    27  	"github.com/juju/juju/juju/keys"
    28  	"github.com/juju/juju/juju/testing"
    29  	"github.com/juju/juju/provider/joyent"
    30  	coretesting "github.com/juju/juju/testing"
    31  	jujuversion "github.com/juju/juju/version"
    32  )
    33  
    34  func registerLocalTests() {
    35  	gc.Suite(&localServerSuite{})
    36  	gc.Suite(&localLiveSuite{})
    37  }
    38  
    39  type localCloudAPIServer struct {
    40  	Server *httptest.Server
    41  }
    42  
    43  func (ca *localCloudAPIServer) setupServer(c *gc.C) {
    44  	// Set up the HTTP server.
    45  	ca.Server = httptest.NewServer(nil)
    46  	c.Assert(ca.Server, gc.NotNil)
    47  	mux := http.NewServeMux()
    48  	ca.Server.Config.Handler = mux
    49  
    50  	cloudapi := lc.New(ca.Server.URL, testUser)
    51  	cloudapi.SetupHTTP(mux)
    52  	c.Logf("Started local CloudAPI service at: %v", ca.Server.URL)
    53  }
    54  
    55  func (s *localCloudAPIServer) destroyServer(c *gc.C) {
    56  	s.Server.Close()
    57  }
    58  
    59  type localLiveSuite struct {
    60  	baseSuite
    61  	jujutest.LiveTests
    62  	cSrv localCloudAPIServer
    63  }
    64  
    65  func (s *localLiveSuite) SetUpSuite(c *gc.C) {
    66  	s.baseSuite.SetUpSuite(c)
    67  	s.LiveTests.SetUpSuite(c)
    68  	s.cSrv.setupServer(c)
    69  	s.AddCleanup(s.cSrv.destroyServer)
    70  
    71  	s.Credential = cloud.NewCredential(cloud.UserPassAuthType, map[string]string{
    72  		"sdc-user":    testUser,
    73  		"sdc-key-id":  testKeyFingerprint,
    74  		"private-key": testPrivateKey,
    75  		"algorithm":   "rsa-sha256",
    76  	})
    77  	s.CloudEndpoint = s.cSrv.Server.URL
    78  	s.CloudRegion = "some-region"
    79  
    80  	s.TestConfig = GetFakeConfig().Merge(coretesting.Attrs{
    81  		"image-metadata-url": "test://host",
    82  	})
    83  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
    84  	s.AddCleanup(func(*gc.C) { envtesting.PatchAttemptStrategies(&joyent.ShortAttempt) })
    85  }
    86  
    87  func (s *localLiveSuite) TearDownSuite(c *gc.C) {
    88  	joyent.UnregisterExternalTestImageMetadata()
    89  	s.LiveTests.TearDownSuite(c)
    90  	s.baseSuite.TearDownSuite(c)
    91  }
    92  
    93  func (s *localLiveSuite) SetUpTest(c *gc.C) {
    94  	s.baseSuite.SetUpTest(c)
    95  	s.LiveTests.SetUpTest(c)
    96  	creds := joyent.MakeCredentials(c, s.CloudEndpoint, s.Credential)
    97  	joyent.UseExternalTestImageMetadata(c, creds)
    98  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "test://host")
    99  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   100  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
   101  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   102  }
   103  
   104  func (s *localLiveSuite) TearDownTest(c *gc.C) {
   105  	s.LiveTests.TearDownTest(c)
   106  	s.baseSuite.TearDownTest(c)
   107  }
   108  
   109  // localServerSuite contains tests that run against an Joyent service double.
   110  // These tests can test things that would be unreasonably slow or expensive
   111  // to test on a live Joyent server. The service double is started and stopped for
   112  // each test.
   113  type localServerSuite struct {
   114  	baseSuite
   115  	jujutest.Tests
   116  	cSrv localCloudAPIServer
   117  }
   118  
   119  func (s *localServerSuite) SetUpSuite(c *gc.C) {
   120  	s.baseSuite.SetUpSuite(c)
   121  	s.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
   122  	s.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
   123  
   124  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   125  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
   126  }
   127  
   128  func (s *localServerSuite) SetUpTest(c *gc.C) {
   129  	s.baseSuite.SetUpTest(c)
   130  
   131  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   132  	s.cSrv.setupServer(c)
   133  	s.AddCleanup(s.cSrv.destroyServer)
   134  	s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })
   135  	s.Tests.SetUpTest(c)
   136  
   137  	s.Credential = cloud.NewCredential(cloud.UserPassAuthType, map[string]string{
   138  		"sdc-user":    testUser,
   139  		"sdc-key-id":  testKeyFingerprint,
   140  		"private-key": testPrivateKey,
   141  		"algorithm":   "rsa-sha256",
   142  	})
   143  	s.CloudEndpoint = s.cSrv.Server.URL
   144  	s.CloudRegion = "some-region"
   145  	s.TestConfig = GetFakeConfig()
   146  
   147  	// Put some fake image metadata in place.
   148  	creds := joyent.MakeCredentials(c, s.CloudEndpoint, s.Credential)
   149  	joyent.UseExternalTestImageMetadata(c, creds)
   150  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "test://host")
   151  }
   152  
   153  func (s *localServerSuite) TearDownTest(c *gc.C) {
   154  	joyent.UnregisterExternalTestImageMetadata()
   155  	s.Tests.TearDownTest(c)
   156  	s.baseSuite.TearDownTest(c)
   157  }
   158  
   159  func bootstrapContext(c *gc.C) environs.BootstrapContext {
   160  	return envtesting.BootstrapContext(c)
   161  }
   162  
   163  // If the environment is configured not to require a public IP address for nodes,
   164  // bootstrapping and starting an instance should occur without any attempt to
   165  // allocate a public address.
   166  func (s *localServerSuite) TestStartInstance(c *gc.C) {
   167  	env := s.Prepare(c)
   168  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{
   169  		ControllerConfig: coretesting.FakeControllerConfig(),
   170  		AdminSecret:      testing.AdminSecret,
   171  		CAPrivateKey:     coretesting.CAKey,
   172  	})
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	inst, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
   175  	err = env.StopInstances(inst.Id())
   176  	c.Assert(err, jc.ErrorIsNil)
   177  }
   178  
   179  func (s *localServerSuite) TestStartInstanceAvailabilityZone(c *gc.C) {
   180  	env := s.Prepare(c)
   181  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{
   182  		ControllerConfig: coretesting.FakeControllerConfig(),
   183  		AdminSecret:      testing.AdminSecret,
   184  		CAPrivateKey:     coretesting.CAKey,
   185  	})
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	inst, hwc := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
   188  	err = env.StopInstances(inst.Id())
   189  	c.Assert(err, jc.ErrorIsNil)
   190  
   191  	c.Check(hwc.AvailabilityZone, gc.IsNil)
   192  }
   193  
   194  func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   195  	env := s.Prepare(c)
   196  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{
   197  		ControllerConfig: coretesting.FakeControllerConfig(),
   198  		AdminSecret:      testing.AdminSecret,
   199  		CAPrivateKey:     coretesting.CAKey,
   200  	})
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	_, hc := testing.AssertStartInstanceWithConstraints(c, env, s.ControllerUUID, "100", constraints.MustParse("mem=1024"))
   203  	c.Check(*hc.Arch, gc.Equals, "amd64")
   204  	c.Check(*hc.Mem, gc.Equals, uint64(1024))
   205  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   206  	c.Assert(hc.CpuPower, gc.IsNil)
   207  }
   208  
   209  var instanceGathering = []struct {
   210  	ids []instance.Id
   211  	err error
   212  }{
   213  	{ids: []instance.Id{"id0"}},
   214  	{ids: []instance.Id{"id0", "id0"}},
   215  	{ids: []instance.Id{"id0", "id1"}},
   216  	{ids: []instance.Id{"id1", "id0"}},
   217  	{ids: []instance.Id{"id1", "id0", "id1"}},
   218  	{
   219  		ids: []instance.Id{""},
   220  		err: environs.ErrNoInstances,
   221  	},
   222  	{
   223  		ids: []instance.Id{"", ""},
   224  		err: environs.ErrNoInstances,
   225  	},
   226  	{
   227  		ids: []instance.Id{"", "", ""},
   228  		err: environs.ErrNoInstances,
   229  	},
   230  	{
   231  		ids: []instance.Id{"id0", ""},
   232  		err: environs.ErrPartialInstances,
   233  	},
   234  	{
   235  		ids: []instance.Id{"", "id1"},
   236  		err: environs.ErrPartialInstances,
   237  	},
   238  	{
   239  		ids: []instance.Id{"id0", "id1", ""},
   240  		err: environs.ErrPartialInstances,
   241  	},
   242  	{
   243  		ids: []instance.Id{"id0", "", "id0"},
   244  		err: environs.ErrPartialInstances,
   245  	},
   246  	{
   247  		ids: []instance.Id{"id0", "id0", ""},
   248  		err: environs.ErrPartialInstances,
   249  	},
   250  	{
   251  		ids: []instance.Id{"", "id0", "id1"},
   252  		err: environs.ErrPartialInstances,
   253  	},
   254  }
   255  
   256  func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
   257  	env := s.Prepare(c)
   258  	inst, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
   259  	c.Assert(inst.Status().Message, gc.Equals, "running")
   260  	err := env.StopInstances(inst.Id())
   261  	c.Assert(err, jc.ErrorIsNil)
   262  }
   263  
   264  func (s *localServerSuite) TestInstancesGathering(c *gc.C) {
   265  	env := s.Prepare(c)
   266  	inst0, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
   267  	id0 := inst0.Id()
   268  	inst1, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "101")
   269  	id1 := inst1.Id()
   270  	c.Logf("id0: %s, id1: %s", id0, id1)
   271  	defer func() {
   272  		// StopInstances deletes machines in parallel but the Joyent
   273  		// API test double isn't goroutine-safe so stop them one at a
   274  		// time. See https://pad.lv/1604514
   275  		c.Check(env.StopInstances(inst0.Id()), jc.ErrorIsNil)
   276  		c.Check(env.StopInstances(inst1.Id()), jc.ErrorIsNil)
   277  	}()
   278  
   279  	for i, test := range instanceGathering {
   280  		c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err)
   281  		ids := make([]instance.Id, len(test.ids))
   282  		for j, id := range test.ids {
   283  			switch id {
   284  			case "id0":
   285  				ids[j] = id0
   286  			case "id1":
   287  				ids[j] = id1
   288  			}
   289  		}
   290  		insts, err := env.Instances(ids)
   291  		c.Assert(err, gc.Equals, test.err)
   292  		if err == environs.ErrNoInstances {
   293  			c.Assert(insts, gc.HasLen, 0)
   294  		} else {
   295  			c.Assert(insts, gc.HasLen, len(test.ids))
   296  		}
   297  		for j, inst := range insts {
   298  			if ids[j] != "" {
   299  				c.Assert(inst.Id(), gc.Equals, ids[j])
   300  			} else {
   301  				c.Assert(inst, gc.IsNil)
   302  			}
   303  		}
   304  	}
   305  }
   306  
   307  // It should be moved to environs.jujutests.Tests.
   308  func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
   309  	env := s.Prepare(c)
   310  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{
   311  		ControllerConfig: coretesting.FakeControllerConfig(),
   312  		AdminSecret:      testing.AdminSecret,
   313  		CAPrivateKey:     coretesting.CAKey,
   314  	})
   315  	c.Assert(err, jc.ErrorIsNil)
   316  
   317  	// check that ControllerInstances returns the id of the bootstrap machine.
   318  	instanceIds, err := env.ControllerInstances(s.ControllerUUID)
   319  	c.Assert(err, jc.ErrorIsNil)
   320  	c.Assert(instanceIds, gc.HasLen, 1)
   321  
   322  	insts, err := env.AllInstances()
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	c.Assert(insts, gc.HasLen, 1)
   325  	c.Check(instanceIds[0], gc.Equals, insts[0].Id())
   326  
   327  	addresses, err := insts[0].Addresses()
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	c.Assert(addresses, gc.HasLen, 2)
   330  }
   331  
   332  func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
   333  	s.PatchValue(&tools.DefaultBaseURL, "")
   334  
   335  	env := s.Prepare(c)
   336  	sources, err := tools.GetMetadataSources(env)
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	c.Assert(sources, gc.HasLen, 0)
   339  }
   340  
   341  func (s *localServerSuite) TestFindInstanceSpec(c *gc.C) {
   342  	env := s.Prepare(c)
   343  	imageMetadata := []*imagemetadata.ImageMetadata{{
   344  		Id:       "image-id",
   345  		Arch:     "amd64",
   346  		VirtType: "kvm",
   347  	}}
   348  	spec, err := joyent.FindInstanceSpec(env, "trusty", "amd64", "mem=4G", imageMetadata)
   349  	c.Assert(err, gc.IsNil)
   350  	c.Assert(spec.InstanceType.VirtType, gc.NotNil)
   351  	c.Check(spec.Image.Arch, gc.Equals, "amd64")
   352  	c.Check(spec.Image.VirtType, gc.Equals, "kvm")
   353  	c.Check(*spec.InstanceType.VirtType, gc.Equals, "kvm")
   354  	c.Check(spec.InstanceType.CpuCores, gc.Equals, uint64(4))
   355  }
   356  
   357  func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
   358  	env := s.Prepare(c)
   359  	// An error occurs if no suitable image is found.
   360  	_, err := joyent.FindInstanceSpec(env, "saucy", "amd64", "mem=4G", nil)
   361  	c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`)
   362  }
   363  
   364  func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
   365  	env := s.Prepare(c)
   366  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region")
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	params.Sources, err = environs.ImageMetadataSources(env)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	params.Series = "raring"
   371  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	c.Assert(image_ids, gc.DeepEquals, []string{"11223344-0a0a-dd77-33cd-abcd1234e5f6"})
   374  }
   375  
   376  func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
   377  	env := s.Prepare(c)
   378  	validator, err := env.ConstraintsValidator()
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	cons := constraints.MustParse("arch=amd64 tags=bar cpu-power=10 virt-type=kvm")
   381  	unsupported, err := validator.Validate(cons)
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "tags", "virt-type"})
   384  }
   385  
   386  func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
   387  	env := s.Prepare(c)
   388  	validator, err := env.ConstraintsValidator()
   389  	c.Assert(err, jc.ErrorIsNil)
   390  	cons := constraints.MustParse("instance-type=foo")
   391  	_, err = validator.Validate(cons)
   392  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
   393  }