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 }