github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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 "bytes" 8 "io/ioutil" 9 "net/http" 10 "net/http/httptest" 11 "strings" 12 13 lm "github.com/joyent/gomanta/localservices/manta" 14 lc "github.com/joyent/gosdc/localservices/cloudapi" 15 jc "github.com/juju/testing/checkers" 16 gc "launchpad.net/gocheck" 17 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/environs/bootstrap" 21 "github.com/juju/juju/environs/imagemetadata" 22 "github.com/juju/juju/environs/jujutest" 23 "github.com/juju/juju/environs/simplestreams" 24 "github.com/juju/juju/environs/storage" 25 envtesting "github.com/juju/juju/environs/testing" 26 "github.com/juju/juju/environs/tools" 27 "github.com/juju/juju/instance" 28 "github.com/juju/juju/juju/arch" 29 "github.com/juju/juju/juju/testing" 30 "github.com/juju/juju/provider/joyent" 31 coretesting "github.com/juju/juju/testing" 32 ) 33 34 func registerLocalTests() { 35 gc.Suite(&localServerSuite{}) 36 gc.Suite(&localLiveSuite{}) 37 } 38 39 type localCloudAPIServer struct { 40 Server *httptest.Server 41 Mux *http.ServeMux 42 oldHandler http.Handler 43 cloudapi *lc.CloudAPI 44 } 45 46 func (ca *localCloudAPIServer) setupServer(c *gc.C) { 47 // Set up the HTTP server. 48 ca.Server = httptest.NewServer(nil) 49 c.Assert(ca.Server, gc.NotNil) 50 ca.oldHandler = ca.Server.Config.Handler 51 ca.Mux = http.NewServeMux() 52 ca.Server.Config.Handler = ca.Mux 53 54 ca.cloudapi = lc.New(ca.Server.URL, testUser) 55 ca.cloudapi.SetupHTTP(ca.Mux) 56 c.Logf("Started local CloudAPI service at: %v", ca.Server.URL) 57 } 58 59 func (c *localCloudAPIServer) destroyServer() { 60 c.Mux = nil 61 c.Server.Config.Handler = c.oldHandler 62 c.Server.Close() 63 } 64 65 type localMantaServer struct { 66 Server *httptest.Server 67 Mux *http.ServeMux 68 oldHandler http.Handler 69 manta *lm.Manta 70 } 71 72 func (m *localMantaServer) setupServer(c *gc.C) { 73 // Set up the HTTP server. 74 m.Server = httptest.NewServer(nil) 75 c.Assert(m.Server, gc.NotNil) 76 m.oldHandler = m.Server.Config.Handler 77 m.Mux = http.NewServeMux() 78 m.Server.Config.Handler = m.Mux 79 80 m.manta = lm.New(m.Server.URL, testUser) 81 m.manta.SetupHTTP(m.Mux) 82 c.Logf("Started local Manta service at: %v", m.Server.URL) 83 } 84 85 func (m *localMantaServer) destroyServer() { 86 m.Mux = nil 87 m.Server.Config.Handler = m.oldHandler 88 m.Server.Close() 89 } 90 91 type localLiveSuite struct { 92 providerSuite 93 jujutest.LiveTests 94 cSrv *localCloudAPIServer 95 mSrv *localMantaServer 96 } 97 98 func (s *localLiveSuite) SetUpSuite(c *gc.C) { 99 s.providerSuite.SetUpSuite(c) 100 s.cSrv = &localCloudAPIServer{} 101 s.mSrv = &localMantaServer{} 102 s.cSrv.setupServer(c) 103 s.mSrv.setupServer(c) 104 s.AddSuiteCleanup(func(*gc.C) { envtesting.PatchAttemptStrategies(&joyent.ShortAttempt) }) 105 106 s.TestConfig = GetFakeConfig(s.cSrv.Server.URL, s.mSrv.Server.URL) 107 s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{ 108 "image-metadata-url": "test://host", 109 }) 110 s.LiveTests.UploadArches = []string{arch.AMD64} 111 s.LiveTests.SetUpSuite(c) 112 } 113 114 func (s *localLiveSuite) TearDownSuite(c *gc.C) { 115 joyent.UnregisterExternalTestImageMetadata() 116 s.LiveTests.TearDownSuite(c) 117 s.cSrv.destroyServer() 118 s.mSrv.destroyServer() 119 s.providerSuite.TearDownSuite(c) 120 } 121 122 func (s *localLiveSuite) SetUpTest(c *gc.C) { 123 s.providerSuite.SetUpTest(c) 124 creds := joyent.MakeCredentials(c, s.TestConfig) 125 joyent.UseExternalTestImageMetadata(creds) 126 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 127 s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() }) 128 s.LiveTests.SetUpTest(c) 129 } 130 131 func (s *localLiveSuite) TearDownTest(c *gc.C) { 132 s.LiveTests.TearDownTest(c) 133 s.providerSuite.TearDownTest(c) 134 } 135 136 // localServerSuite contains tests that run against an Joyent service double. 137 // These tests can test things that would be unreasonably slow or expensive 138 // to test on a live Joyent server. The service double is started and stopped for 139 // each test. 140 type localServerSuite struct { 141 providerSuite 142 jujutest.Tests 143 cSrv *localCloudAPIServer 144 mSrv *localMantaServer 145 } 146 147 func (s *localServerSuite) SetUpSuite(c *gc.C) { 148 s.providerSuite.SetUpSuite(c) 149 restoreFinishBootstrap := envtesting.DisableFinishBootstrap() 150 s.AddSuiteCleanup(func(*gc.C) { restoreFinishBootstrap() }) 151 } 152 153 func (s *localServerSuite) SetUpTest(c *gc.C) { 154 s.providerSuite.SetUpTest(c) 155 156 s.cSrv = &localCloudAPIServer{} 157 s.mSrv = &localMantaServer{} 158 s.cSrv.setupServer(c) 159 s.mSrv.setupServer(c) 160 161 s.Tests.ToolsFixture.UploadArches = []string{arch.AMD64} 162 s.Tests.SetUpTest(c) 163 s.TestConfig = GetFakeConfig(s.cSrv.Server.URL, s.mSrv.Server.URL) 164 // Put some fake image metadata in place. 165 creds := joyent.MakeCredentials(c, s.TestConfig) 166 joyent.UseExternalTestImageMetadata(creds) 167 } 168 169 func (s *localServerSuite) TearDownTest(c *gc.C) { 170 joyent.UnregisterExternalTestImageMetadata() 171 s.Tests.TearDownTest(c) 172 s.cSrv.destroyServer() 173 s.mSrv.destroyServer() 174 s.providerSuite.TearDownTest(c) 175 } 176 177 func bootstrapContext(c *gc.C) environs.BootstrapContext { 178 return coretesting.Context(c) 179 } 180 181 // If the environment is configured not to require a public IP address for nodes, 182 // bootstrapping and starting an instance should occur without any attempt to 183 // allocate a public address. 184 func (s *localServerSuite) TestStartInstance(c *gc.C) { 185 env := s.Prepare(c) 186 s.Tests.UploadFakeTools(c, env.Storage()) 187 err := bootstrap.Bootstrap(bootstrapContext(c), env, environs.BootstrapParams{}) 188 c.Assert(err, gc.IsNil) 189 inst, _ := testing.AssertStartInstance(c, env, "100") 190 err = env.StopInstances(inst.Id()) 191 c.Assert(err, gc.IsNil) 192 } 193 194 func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) { 195 env := s.Prepare(c) 196 s.Tests.UploadFakeTools(c, env.Storage()) 197 err := bootstrap.Bootstrap(bootstrapContext(c), env, environs.BootstrapParams{}) 198 c.Assert(err, gc.IsNil) 199 _, hc := testing.AssertStartInstanceWithConstraints(c, env, "100", constraints.MustParse("mem=1024")) 200 c.Check(*hc.Arch, gc.Equals, "amd64") 201 c.Check(*hc.Mem, gc.Equals, uint64(1024)) 202 c.Check(*hc.CpuCores, gc.Equals, uint64(1)) 203 c.Assert(hc.CpuPower, gc.IsNil) 204 } 205 206 var instanceGathering = []struct { 207 ids []instance.Id 208 err error 209 }{ 210 {ids: []instance.Id{"id0"}}, 211 {ids: []instance.Id{"id0", "id0"}}, 212 {ids: []instance.Id{"id0", "id1"}}, 213 {ids: []instance.Id{"id1", "id0"}}, 214 {ids: []instance.Id{"id1", "id0", "id1"}}, 215 { 216 ids: []instance.Id{""}, 217 err: environs.ErrNoInstances, 218 }, 219 { 220 ids: []instance.Id{"", ""}, 221 err: environs.ErrNoInstances, 222 }, 223 { 224 ids: []instance.Id{"", "", ""}, 225 err: environs.ErrNoInstances, 226 }, 227 { 228 ids: []instance.Id{"id0", ""}, 229 err: environs.ErrPartialInstances, 230 }, 231 { 232 ids: []instance.Id{"", "id1"}, 233 err: environs.ErrPartialInstances, 234 }, 235 { 236 ids: []instance.Id{"id0", "id1", ""}, 237 err: environs.ErrPartialInstances, 238 }, 239 { 240 ids: []instance.Id{"id0", "", "id0"}, 241 err: environs.ErrPartialInstances, 242 }, 243 { 244 ids: []instance.Id{"id0", "id0", ""}, 245 err: environs.ErrPartialInstances, 246 }, 247 { 248 ids: []instance.Id{"", "id0", "id1"}, 249 err: environs.ErrPartialInstances, 250 }, 251 } 252 253 func (s *localServerSuite) TestInstanceStatus(c *gc.C) { 254 env := s.Prepare(c) 255 s.Tests.UploadFakeTools(c, env.Storage()) 256 inst, _ := testing.AssertStartInstance(c, env, "100") 257 c.Assert(inst.Status(), gc.Equals, "running") 258 err := env.StopInstances(inst.Id()) 259 c.Assert(err, gc.IsNil) 260 } 261 262 func (s *localServerSuite) TestInstancesGathering(c *gc.C) { 263 env := s.Prepare(c) 264 s.Tests.UploadFakeTools(c, env.Storage()) 265 inst0, _ := testing.AssertStartInstance(c, env, "100") 266 id0 := inst0.Id() 267 inst1, _ := testing.AssertStartInstance(c, env, "101") 268 id1 := inst1.Id() 269 c.Logf("id0: %s, id1: %s", id0, id1) 270 defer func() { 271 err := env.StopInstances(inst0.Id(), inst1.Id()) 272 c.Assert(err, gc.IsNil) 273 }() 274 275 for i, test := range instanceGathering { 276 c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err) 277 ids := make([]instance.Id, len(test.ids)) 278 for j, id := range test.ids { 279 switch id { 280 case "id0": 281 ids[j] = id0 282 case "id1": 283 ids[j] = id1 284 } 285 } 286 insts, err := env.Instances(ids) 287 c.Assert(err, gc.Equals, test.err) 288 if err == environs.ErrNoInstances { 289 c.Assert(insts, gc.HasLen, 0) 290 } else { 291 c.Assert(insts, gc.HasLen, len(test.ids)) 292 } 293 for j, inst := range insts { 294 if ids[j] != "" { 295 c.Assert(inst.Id(), gc.Equals, ids[j]) 296 } else { 297 c.Assert(inst, gc.IsNil) 298 } 299 } 300 } 301 } 302 303 // It should be moved to environs.jujutests.Tests. 304 func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) { 305 env := s.Prepare(c) 306 s.Tests.UploadFakeTools(c, env.Storage()) 307 err := bootstrap.Bootstrap(bootstrapContext(c), env, environs.BootstrapParams{}) 308 c.Assert(err, gc.IsNil) 309 310 // check that the state holds the id of the bootstrap machine. 311 stateData, err := bootstrap.LoadState(env.Storage()) 312 c.Assert(err, gc.IsNil) 313 c.Assert(stateData.StateInstances, gc.HasLen, 1) 314 315 insts, err := env.AllInstances() 316 c.Assert(err, gc.IsNil) 317 c.Assert(insts, gc.HasLen, 1) 318 c.Check(stateData.StateInstances[0], gc.Equals, insts[0].Id()) 319 320 addresses, err := insts[0].Addresses() 321 c.Assert(err, gc.IsNil) 322 c.Assert(addresses, gc.Not(gc.HasLen), 0) 323 } 324 325 func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) { 326 env := s.Prepare(c) 327 sources, err := imagemetadata.GetMetadataSources(env) 328 c.Assert(err, gc.IsNil) 329 c.Assert(len(sources), gc.Equals, 2) 330 var urls = make([]string, len(sources)) 331 for i, source := range sources { 332 url, err := source.URL("") 333 c.Assert(err, gc.IsNil) 334 urls[i] = url 335 } 336 // The control bucket URL contains the bucket name. 337 c.Assert(strings.Contains(urls[0], joyent.ControlBucketName(env)+"/images"), jc.IsTrue) 338 } 339 340 func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) { 341 env := s.Prepare(c) 342 sources, err := tools.GetMetadataSources(env) 343 c.Assert(err, gc.IsNil) 344 c.Assert(len(sources), gc.Equals, 1) 345 var urls = make([]string, len(sources)) 346 for i, source := range sources { 347 url, err := source.URL("") 348 c.Assert(err, gc.IsNil) 349 urls[i] = url 350 } 351 // The control bucket URL contains the bucket name. 352 c.Assert(strings.Contains(urls[0], joyent.ControlBucketName(env)+"/tools"), jc.IsTrue) 353 } 354 355 func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) { 356 env := s.Prepare(c) 357 // An error occurs if no suitable image is found. 358 _, err := joyent.FindInstanceSpec(env, "saucy", "amd64", "mem=4G") 359 c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`) 360 } 361 362 func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) { 363 env := s.Prepare(c) 364 params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region") 365 c.Assert(err, gc.IsNil) 366 params.Sources, err = imagemetadata.GetMetadataSources(env) 367 c.Assert(err, gc.IsNil) 368 params.Series = "raring" 369 image_ids, _, err := imagemetadata.ValidateImageMetadata(params) 370 c.Assert(err, gc.IsNil) 371 c.Assert(image_ids, gc.DeepEquals, []string{"11223344-0a0a-dd77-33cd-abcd1234e5f6"}) 372 } 373 374 func (s *localServerSuite) TestRemoveAll(c *gc.C) { 375 env := s.Prepare(c) 376 stor := env.Storage() 377 for _, a := range []byte("abcdefghijklmnopqrstuvwxyz") { 378 content := []byte{a} 379 name := string(content) 380 err := stor.Put(name, bytes.NewBuffer(content), 381 int64(len(content))) 382 c.Assert(err, gc.IsNil) 383 } 384 reader, err := storage.Get(stor, "a") 385 c.Assert(err, gc.IsNil) 386 allContent, err := ioutil.ReadAll(reader) 387 c.Assert(err, gc.IsNil) 388 c.Assert(string(allContent), gc.Equals, "a") 389 err = stor.RemoveAll() 390 c.Assert(err, gc.IsNil) 391 _, err = storage.Get(stor, "a") 392 c.Assert(err, gc.NotNil) 393 } 394 395 func (s *localServerSuite) TestDeleteMoreThan100(c *gc.C) { 396 env := s.Prepare(c) 397 stor := env.Storage() 398 // 6*26 = 156 items 399 for _, a := range []byte("abcdef") { 400 for _, b := range []byte("abcdefghijklmnopqrstuvwxyz") { 401 content := []byte{a, b} 402 name := string(content) 403 err := stor.Put(name, bytes.NewBuffer(content), 404 int64(len(content))) 405 c.Assert(err, gc.IsNil) 406 } 407 } 408 reader, err := storage.Get(stor, "ab") 409 c.Assert(err, gc.IsNil) 410 allContent, err := ioutil.ReadAll(reader) 411 c.Assert(err, gc.IsNil) 412 c.Assert(string(allContent), gc.Equals, "ab") 413 err = stor.RemoveAll() 414 c.Assert(err, gc.IsNil) 415 _, err = storage.Get(stor, "ab") 416 c.Assert(err, gc.NotNil) 417 } 418 419 func (s *localServerSuite) TestConstraintsValidator(c *gc.C) { 420 env := s.Prepare(c) 421 validator, err := env.ConstraintsValidator() 422 c.Assert(err, gc.IsNil) 423 cons := constraints.MustParse("arch=amd64 tags=bar cpu-power=10") 424 unsupported, err := validator.Validate(cons) 425 c.Assert(err, gc.IsNil) 426 c.Assert(unsupported, jc.SameContents, []string{"cpu-power", "tags"}) 427 } 428 429 func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) { 430 env := s.Prepare(c) 431 validator, err := env.ConstraintsValidator() 432 c.Assert(err, gc.IsNil) 433 cons := constraints.MustParse("arch=ppc64") 434 _, err = validator.Validate(cons) 435 c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=ppc64\nvalid values are:.*") 436 cons = constraints.MustParse("instance-type=foo") 437 _, err = validator.Validate(cons) 438 c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*") 439 }