github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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"
    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  	providerSuite
    61  	jujutest.LiveTests
    62  	cSrv localCloudAPIServer
    63  }
    64  
    65  func (s *localLiveSuite) SetUpSuite(c *gc.C) {
    66  	s.providerSuite.SetUpSuite(c)
    67  	s.LiveTests.SetUpSuite(c)
    68  	s.cSrv.setupServer(c)
    69  	s.AddCleanup(s.cSrv.destroyServer)
    70  
    71  	s.TestConfig = GetFakeConfig(s.cSrv.Server.URL)
    72  	s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{
    73  		"image-metadata-url": "test://host",
    74  	})
    75  	s.LiveTests.UploadArches = []string{arch.AMD64}
    76  	s.AddCleanup(func(*gc.C) { envtesting.PatchAttemptStrategies(&joyent.ShortAttempt) })
    77  }
    78  
    79  func (s *localLiveSuite) TearDownSuite(c *gc.C) {
    80  	joyent.UnregisterExternalTestImageMetadata()
    81  	s.LiveTests.TearDownSuite(c)
    82  	s.providerSuite.TearDownSuite(c)
    83  }
    84  
    85  func (s *localLiveSuite) SetUpTest(c *gc.C) {
    86  	s.providerSuite.SetUpTest(c)
    87  	s.LiveTests.SetUpTest(c)
    88  	credentialsAttrs := joyent.CredentialsAttributes(s.TestConfig)
    89  	s.Credential = cloud.NewCredential(
    90  		cloud.UserPassAuthType,
    91  		credentialsAttrs,
    92  	)
    93  	creds := joyent.MakeCredentials(c, s.TestConfig)
    94  	joyent.UseExternalTestImageMetadata(c, creds)
    95  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "test://host")
    96  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
    97  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
    98  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
    99  }
   100  
   101  func (s *localLiveSuite) TearDownTest(c *gc.C) {
   102  	s.LiveTests.TearDownTest(c)
   103  	s.providerSuite.TearDownTest(c)
   104  }
   105  
   106  // localServerSuite contains tests that run against an Joyent service double.
   107  // These tests can test things that would be unreasonably slow or expensive
   108  // to test on a live Joyent server. The service double is started and stopped for
   109  // each test.
   110  type localServerSuite struct {
   111  	providerSuite
   112  	jujutest.Tests
   113  	cSrv localCloudAPIServer
   114  }
   115  
   116  func (s *localServerSuite) SetUpSuite(c *gc.C) {
   117  	s.providerSuite.SetUpSuite(c)
   118  	s.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
   119  	s.PatchValue(&juju.JujuPublicKey, sstesting.SignedMetadataPublicKey)
   120  
   121  	restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
   122  	s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
   123  }
   124  
   125  func (s *localServerSuite) SetUpTest(c *gc.C) {
   126  	s.providerSuite.SetUpTest(c)
   127  
   128  	s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
   129  	s.cSrv.setupServer(c)
   130  	s.AddCleanup(s.cSrv.destroyServer)
   131  
   132  	s.Tests.ToolsFixture.UploadArches = []string{arch.AMD64}
   133  	s.Tests.SetUpTest(c)
   134  	s.TestConfig = GetFakeConfig(s.cSrv.Server.URL)
   135  	credentialsAttrs := joyent.CredentialsAttributes(s.TestConfig)
   136  	s.Credential = cloud.NewCredential(
   137  		cloud.UserPassAuthType,
   138  		credentialsAttrs,
   139  	)
   140  	// Put some fake image metadata in place.
   141  	creds := joyent.MakeCredentials(c, s.TestConfig)
   142  	joyent.UseExternalTestImageMetadata(c, creds)
   143  	imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "test://host")
   144  }
   145  
   146  func (s *localServerSuite) TearDownTest(c *gc.C) {
   147  	joyent.UnregisterExternalTestImageMetadata()
   148  	s.Tests.TearDownTest(c)
   149  	s.providerSuite.TearDownTest(c)
   150  }
   151  
   152  func bootstrapContext(c *gc.C) environs.BootstrapContext {
   153  	return envtesting.BootstrapContext(c)
   154  }
   155  
   156  // If the environment is configured not to require a public IP address for nodes,
   157  // bootstrapping and starting an instance should occur without any attempt to
   158  // allocate a public address.
   159  func (s *localServerSuite) TestStartInstance(c *gc.C) {
   160  	env := s.Prepare(c)
   161  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{})
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	inst, _ := testing.AssertStartInstance(c, env, "100")
   164  	err = env.StopInstances(inst.Id())
   165  	c.Assert(err, jc.ErrorIsNil)
   166  }
   167  
   168  func (s *localServerSuite) TestStartInstanceAvailabilityZone(c *gc.C) {
   169  	env := s.Prepare(c)
   170  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{})
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	inst, hwc := testing.AssertStartInstance(c, env, "100")
   173  	err = env.StopInstances(inst.Id())
   174  	c.Assert(err, jc.ErrorIsNil)
   175  
   176  	c.Check(hwc.AvailabilityZone, gc.IsNil)
   177  }
   178  
   179  func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
   180  	env := s.Prepare(c)
   181  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{})
   182  	c.Assert(err, jc.ErrorIsNil)
   183  	_, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024"))
   184  	c.Check(*hc.Arch, gc.Equals, "amd64")
   185  	c.Check(*hc.Mem, gc.Equals, uint64(1024))
   186  	c.Check(*hc.CpuCores, gc.Equals, uint64(1))
   187  	c.Assert(hc.CpuPower, gc.IsNil)
   188  }
   189  
   190  var instanceGathering = []struct {
   191  	ids []instance.Id
   192  	err error
   193  }{
   194  	{ids: []instance.Id{"id0"}},
   195  	{ids: []instance.Id{"id0", "id0"}},
   196  	{ids: []instance.Id{"id0", "id1"}},
   197  	{ids: []instance.Id{"id1", "id0"}},
   198  	{ids: []instance.Id{"id1", "id0", "id1"}},
   199  	{
   200  		ids: []instance.Id{""},
   201  		err: environs.ErrNoInstances,
   202  	},
   203  	{
   204  		ids: []instance.Id{"", ""},
   205  		err: environs.ErrNoInstances,
   206  	},
   207  	{
   208  		ids: []instance.Id{"", "", ""},
   209  		err: environs.ErrNoInstances,
   210  	},
   211  	{
   212  		ids: []instance.Id{"id0", ""},
   213  		err: environs.ErrPartialInstances,
   214  	},
   215  	{
   216  		ids: []instance.Id{"", "id1"},
   217  		err: environs.ErrPartialInstances,
   218  	},
   219  	{
   220  		ids: []instance.Id{"id0", "id1", ""},
   221  		err: environs.ErrPartialInstances,
   222  	},
   223  	{
   224  		ids: []instance.Id{"id0", "", "id0"},
   225  		err: environs.ErrPartialInstances,
   226  	},
   227  	{
   228  		ids: []instance.Id{"id0", "id0", ""},
   229  		err: environs.ErrPartialInstances,
   230  	},
   231  	{
   232  		ids: []instance.Id{"", "id0", "id1"},
   233  		err: environs.ErrPartialInstances,
   234  	},
   235  }
   236  
   237  func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
   238  	env := s.Prepare(c)
   239  	inst, _ := testing.AssertStartInstance(c, env, "100")
   240  	c.Assert(inst.Status().Message, gc.Equals, "running")
   241  	err := env.StopInstances(inst.Id())
   242  	c.Assert(err, jc.ErrorIsNil)
   243  }
   244  
   245  func (s *localServerSuite) TestInstancesGathering(c *gc.C) {
   246  	env := s.Prepare(c)
   247  	inst0, _ := testing.AssertStartInstance(c, env, "100")
   248  	id0 := inst0.Id()
   249  	inst1, _ := testing.AssertStartInstance(c, env, "101")
   250  	id1 := inst1.Id()
   251  	c.Logf("id0: %s, id1: %s", id0, id1)
   252  	defer func() {
   253  		err := env.StopInstances(inst0.Id(), inst1.Id())
   254  		c.Assert(err, jc.ErrorIsNil)
   255  	}()
   256  
   257  	for i, test := range instanceGathering {
   258  		c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err)
   259  		ids := make([]instance.Id, len(test.ids))
   260  		for j, id := range test.ids {
   261  			switch id {
   262  			case "id0":
   263  				ids[j] = id0
   264  			case "id1":
   265  				ids[j] = id1
   266  			}
   267  		}
   268  		insts, err := env.Instances(ids)
   269  		c.Assert(err, gc.Equals, test.err)
   270  		if err == environs.ErrNoInstances {
   271  			c.Assert(insts, gc.HasLen, 0)
   272  		} else {
   273  			c.Assert(insts, gc.HasLen, len(test.ids))
   274  		}
   275  		for j, inst := range insts {
   276  			if ids[j] != "" {
   277  				c.Assert(inst.Id(), gc.Equals, ids[j])
   278  			} else {
   279  				c.Assert(inst, gc.IsNil)
   280  			}
   281  		}
   282  	}
   283  }
   284  
   285  // It should be moved to environs.jujutests.Tests.
   286  func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
   287  	env := s.Prepare(c)
   288  	err := bootstrap.Bootstrap(bootstrapContext(c), env, bootstrap.BootstrapParams{})
   289  	c.Assert(err, jc.ErrorIsNil)
   290  
   291  	// check that ControllerInstances returns the id of the bootstrap machine.
   292  	instanceIds, err := env.ControllerInstances()
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	c.Assert(instanceIds, gc.HasLen, 1)
   295  
   296  	insts, err := env.AllInstances()
   297  	c.Assert(err, jc.ErrorIsNil)
   298  	c.Assert(insts, gc.HasLen, 1)
   299  	c.Check(instanceIds[0], gc.Equals, insts[0].Id())
   300  
   301  	addresses, err := insts[0].Addresses()
   302  	c.Assert(err, jc.ErrorIsNil)
   303  	c.Assert(addresses, gc.HasLen, 2)
   304  }
   305  
   306  func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
   307  	s.PatchValue(&tools.DefaultBaseURL, "")
   308  
   309  	env := s.Prepare(c)
   310  	sources, err := tools.GetMetadataSources(env)
   311  	c.Assert(err, jc.ErrorIsNil)
   312  	c.Assert(sources, gc.HasLen, 0)
   313  }
   314  
   315  func (s *localServerSuite) TestFindInstanceSpec(c *gc.C) {
   316  	env := s.Prepare(c)
   317  	imageMetadata := []*imagemetadata.ImageMetadata{{
   318  		Id:       "image-id",
   319  		Arch:     "amd64",
   320  		VirtType: "kvm",
   321  	}}
   322  	spec, err := joyent.FindInstanceSpec(env, "trusty", "amd64", "mem=4G", imageMetadata)
   323  	c.Assert(err, gc.IsNil)
   324  	c.Assert(spec.InstanceType.VirtType, gc.NotNil)
   325  	c.Check(spec.Image.Arch, gc.Equals, "amd64")
   326  	c.Check(spec.Image.VirtType, gc.Equals, "kvm")
   327  	c.Check(*spec.InstanceType.VirtType, gc.Equals, "kvm")
   328  	c.Check(spec.InstanceType.CpuCores, gc.Equals, uint64(4))
   329  }
   330  
   331  func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
   332  	env := s.Prepare(c)
   333  	// An error occurs if no suitable image is found.
   334  	_, err := joyent.FindInstanceSpec(env, "saucy", "amd64", "mem=4G", nil)
   335  	c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`)
   336  }
   337  
   338  func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
   339  	env := s.Prepare(c)
   340  	params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region")
   341  	c.Assert(err, jc.ErrorIsNil)
   342  	params.Sources, err = environs.ImageMetadataSources(env)
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	params.Series = "raring"
   345  	image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	c.Assert(image_ids, gc.DeepEquals, []string{"11223344-0a0a-dd77-33cd-abcd1234e5f6"})
   348  }
   349  
   350  func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
   351  	env := s.Prepare(c)
   352  	validator, err := env.ConstraintsValidator()
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	cons := constraints.MustParse("arch=amd64 tags=bar cpu-power=10 virt-type=kvm")
   355  	unsupported, err := validator.Validate(cons)
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "tags", "virt-type"})
   358  }
   359  
   360  func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
   361  	env := s.Prepare(c)
   362  	validator, err := env.ConstraintsValidator()
   363  	c.Assert(err, jc.ErrorIsNil)
   364  	cons := constraints.MustParse("arch=ppc64el")
   365  	_, err = validator.Validate(cons)
   366  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64el\nvalid values are:.*")
   367  	cons = constraints.MustParse("instance-type=foo")
   368  	_, err = validator.Validate(cons)
   369  	c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
   370  }