github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/server_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver_test 5 6 import ( 7 "crypto/tls" 8 "crypto/x509" 9 "fmt" 10 "net" 11 "net/http" 12 "time" 13 14 "github.com/juju/loggo" 15 "github.com/juju/testing" 16 jc "github.com/juju/testing/checkers" 17 "github.com/juju/utils" 18 "github.com/juju/utils/clock" 19 "golang.org/x/net/websocket" 20 gc "gopkg.in/check.v1" 21 "gopkg.in/juju/names.v2" 22 "gopkg.in/macaroon-bakery.v1/bakery" 23 "gopkg.in/macaroon-bakery.v1/bakery/checkers" 24 "gopkg.in/macaroon-bakery.v1/bakerytest" 25 "gopkg.in/macaroon-bakery.v1/httpbakery" 26 27 "github.com/juju/juju/api" 28 apimachiner "github.com/juju/juju/api/machiner" 29 "github.com/juju/juju/apiserver" 30 "github.com/juju/juju/apiserver/observer" 31 "github.com/juju/juju/apiserver/observer/fakeobserver" 32 "github.com/juju/juju/apiserver/params" 33 "github.com/juju/juju/cert" 34 "github.com/juju/juju/controller" 35 jujutesting "github.com/juju/juju/juju/testing" 36 "github.com/juju/juju/mongo" 37 "github.com/juju/juju/network" 38 "github.com/juju/juju/permission" 39 "github.com/juju/juju/state" 40 "github.com/juju/juju/state/presence" 41 coretesting "github.com/juju/juju/testing" 42 "github.com/juju/juju/testing/factory" 43 "github.com/juju/juju/worker/workertest" 44 ) 45 46 var fastDialOpts = api.DialOpts{} 47 48 type serverSuite struct { 49 jujutesting.JujuConnSuite 50 } 51 52 var _ = gc.Suite(&serverSuite{}) 53 54 func (s *serverSuite) TestStop(c *gc.C) { 55 // Start our own instance of the server so we have 56 // a handle on it to stop it. 57 _, srv := newServer(c, s.State) 58 defer assertStop(c, srv) 59 60 machine, password := s.Factory.MakeMachineReturningPassword( 61 c, &factory.MachineParams{Nonce: "fake_nonce"}) 62 63 // A net.TCPAddr cannot be directly stringified into a valid hostname. 64 address := fmt.Sprintf("localhost:%d", srv.Addr().Port) 65 66 // Note we can't use openAs because we're not connecting to 67 apiInfo := &api.Info{ 68 Tag: machine.Tag(), 69 Password: password, 70 Nonce: "fake_nonce", 71 Addrs: []string{address}, 72 CACert: coretesting.CACert, 73 ModelTag: s.State.ModelTag(), 74 } 75 st, err := api.Open(apiInfo, fastDialOpts) 76 c.Assert(err, jc.ErrorIsNil) 77 defer st.Close() 78 79 _, err = apimachiner.NewState(st).Machine(machine.MachineTag()) 80 c.Assert(err, jc.ErrorIsNil) 81 82 err = srv.Stop() 83 c.Assert(err, jc.ErrorIsNil) 84 85 _, err = apimachiner.NewState(st).Machine(machine.MachineTag()) 86 // The client has not necessarily seen the server shutdown yet, so there 87 // are multiple possible errors. All we should care about is that there is 88 // an error, not what the error actually is. 89 c.Assert(err, gc.NotNil) 90 91 // Check it can be stopped twice. 92 err = srv.Stop() 93 c.Assert(err, jc.ErrorIsNil) 94 } 95 96 func (s *serverSuite) TestAPIServerCanListenOnBothIPv4AndIPv6(c *gc.C) { 97 err := s.State.SetAPIHostPorts(nil) 98 c.Assert(err, jc.ErrorIsNil) 99 100 // Start our own instance of the server listening on 101 // both IPv4 and IPv6 localhost addresses and an ephemeral port. 102 _, srv := newServer(c, s.State) 103 defer assertStop(c, srv) 104 105 port := srv.Addr().Port 106 portString := fmt.Sprintf("%d", port) 107 108 machine, password := s.Factory.MakeMachineReturningPassword( 109 c, &factory.MachineParams{Nonce: "fake_nonce"}) 110 111 // Now connect twice - using IPv4 and IPv6 endpoints. 112 apiInfo := &api.Info{ 113 Tag: machine.Tag(), 114 Password: password, 115 Nonce: "fake_nonce", 116 Addrs: []string{net.JoinHostPort("127.0.0.1", portString)}, 117 CACert: coretesting.CACert, 118 ModelTag: s.State.ModelTag(), 119 } 120 ipv4State, err := api.Open(apiInfo, fastDialOpts) 121 c.Assert(err, jc.ErrorIsNil) 122 defer ipv4State.Close() 123 c.Assert(ipv4State.Addr(), gc.Equals, net.JoinHostPort("127.0.0.1", portString)) 124 c.Assert(ipv4State.APIHostPorts(), jc.DeepEquals, [][]network.HostPort{ 125 network.NewHostPorts(port, "127.0.0.1"), 126 }) 127 128 _, err = apimachiner.NewState(ipv4State).Machine(machine.MachineTag()) 129 c.Assert(err, jc.ErrorIsNil) 130 131 apiInfo.Addrs = []string{net.JoinHostPort("::1", portString)} 132 ipv6State, err := api.Open(apiInfo, fastDialOpts) 133 c.Assert(err, jc.ErrorIsNil) 134 defer ipv6State.Close() 135 c.Assert(ipv6State.Addr(), gc.Equals, net.JoinHostPort("::1", portString)) 136 c.Assert(ipv6State.APIHostPorts(), jc.DeepEquals, [][]network.HostPort{ 137 network.NewHostPorts(port, "::1"), 138 }) 139 140 _, err = apimachiner.NewState(ipv6State).Machine(machine.MachineTag()) 141 c.Assert(err, jc.ErrorIsNil) 142 } 143 144 func (s *serverSuite) TestOpenAsMachineErrors(c *gc.C) { 145 assertNotProvisioned := func(err error) { 146 c.Assert(err, gc.NotNil) 147 c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned) 148 c.Assert(err, gc.ErrorMatches, `machine \d+ not provisioned \(not provisioned\)`) 149 } 150 151 machine, password := s.Factory.MakeMachineReturningPassword( 152 c, &factory.MachineParams{Nonce: "fake_nonce"}) 153 154 // This does almost exactly the same as OpenAPIAsMachine but checks 155 // for failures instead. 156 info := s.APIInfo(c) 157 info.Tag = machine.Tag() 158 info.Password = password 159 info.Nonce = "invalid-nonce" 160 st, err := api.Open(info, fastDialOpts) 161 assertNotProvisioned(err) 162 c.Assert(st, gc.IsNil) 163 164 // Try with empty nonce as well. 165 info.Nonce = "" 166 st, err = api.Open(info, fastDialOpts) 167 assertNotProvisioned(err) 168 c.Assert(st, gc.IsNil) 169 170 // Finally, with the correct one succeeds. 171 info.Nonce = "fake_nonce" 172 st, err = api.Open(info, fastDialOpts) 173 c.Assert(err, jc.ErrorIsNil) 174 c.Assert(st, gc.NotNil) 175 st.Close() 176 177 // Now add another machine, intentionally unprovisioned. 178 stm1, err := s.State.AddMachine("quantal", state.JobHostUnits) 179 c.Assert(err, jc.ErrorIsNil) 180 err = stm1.SetPassword(password) 181 c.Assert(err, jc.ErrorIsNil) 182 183 // Try connecting, it will fail. 184 info.Tag = stm1.Tag() 185 info.Nonce = "" 186 st, err = api.Open(info, fastDialOpts) 187 assertNotProvisioned(err) 188 c.Assert(st, gc.IsNil) 189 } 190 191 func (s *serverSuite) TestNewServerDoesNotAccessState(c *gc.C) { 192 mongoInfo := s.MongoInfo(c) 193 194 proxy := testing.NewTCPProxy(c, mongoInfo.Addrs[0]) 195 mongoInfo.Addrs = []string{proxy.Addr()} 196 197 dialOpts := mongo.DialOpts{ 198 Timeout: 5 * time.Second, 199 SocketTimeout: 5 * time.Second, 200 } 201 st, err := state.Open(s.State.ModelTag(), s.State.ControllerTag(), mongoInfo, dialOpts, nil) 202 c.Assert(err, gc.IsNil) 203 defer st.Close() 204 205 // Now close the proxy so that any attempts to use the 206 // controller will fail. 207 proxy.Close() 208 209 // Creating the server should succeed because it doesn't 210 // access the state (note that newServer does not log in, 211 // which *would* access the state). 212 _, srv := newServer(c, st) 213 srv.Stop() 214 } 215 216 func (s *serverSuite) TestMachineLoginStartsPinger(c *gc.C) { 217 // This is the same steps as OpenAPIAsNewMachine but we need to assert 218 // the agent is not alive before we actually open the API. 219 // Create a new machine to verify "agent alive" behavior. 220 machine, password := s.Factory.MakeMachineReturningPassword( 221 c, &factory.MachineParams{Nonce: "fake_nonce"}) 222 223 // Not alive yet. 224 s.assertAlive(c, machine, false) 225 226 // Login as the machine agent of the created machine. 227 st := s.OpenAPIAsMachine(c, machine.Tag(), password, "fake_nonce") 228 defer func() { 229 err := st.Close() 230 c.Check(err, jc.ErrorIsNil) 231 }() 232 233 // Make sure the pinger has started. 234 s.assertAlive(c, machine, true) 235 } 236 237 func (s *serverSuite) TestUnitLoginStartsPinger(c *gc.C) { 238 // Create a new service and unit to verify "agent alive" behavior. 239 unit, password := s.Factory.MakeUnitReturningPassword(c, nil) 240 241 // Not alive yet. 242 s.assertAlive(c, unit, false) 243 244 // Login as the unit agent of the created unit. 245 st := s.OpenAPIAs(c, unit.Tag(), password) 246 defer func() { 247 err := st.Close() 248 c.Check(err, jc.ErrorIsNil) 249 }() 250 251 // Make sure the pinger has started. 252 s.assertAlive(c, unit, true) 253 } 254 255 func (s *serverSuite) assertAlive(c *gc.C, entity presence.Agent, expectAlive bool) { 256 s.State.StartSync() 257 alive, err := entity.AgentPresence() 258 c.Assert(err, jc.ErrorIsNil) 259 c.Assert(alive, gc.Equals, expectAlive) 260 } 261 262 func dialWebsocket(c *gc.C, addr, path string, tlsVersion uint16) (*websocket.Conn, error) { 263 origin := "http://localhost/" 264 url := fmt.Sprintf("wss://%s%s", addr, path) 265 config, err := websocket.NewConfig(url, origin) 266 c.Assert(err, jc.ErrorIsNil) 267 pool := x509.NewCertPool() 268 xcert, err := cert.ParseCert(coretesting.CACert) 269 c.Assert(err, jc.ErrorIsNil) 270 pool.AddCert(xcert) 271 config.TlsConfig = utils.SecureTLSConfig() 272 if tlsVersion > 0 { 273 // This is for testing only. Please don't muck with the maxtlsversion in 274 // production. 275 config.TlsConfig.MaxVersion = tlsVersion 276 } 277 config.TlsConfig.RootCAs = pool 278 return websocket.DialConfig(config) 279 } 280 281 func (s *serverSuite) TestMinTLSVersion(c *gc.C) { 282 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 283 _, srv := newServer(c, s.State) 284 defer assertStop(c, srv) 285 286 // We have to use 'localhost' because that is what the TLS cert says. 287 addr := fmt.Sprintf("localhost:%d", srv.Addr().Port) 288 289 // Specify an unsupported TLS version 290 conn, err := dialWebsocket(c, addr, "/", tls.VersionSSL30) 291 c.Assert(err, gc.ErrorMatches, ".*protocol version not supported") 292 c.Assert(conn, gc.IsNil) 293 } 294 295 func (s *serverSuite) TestNonCompatiblePathsAre404(c *gc.C) { 296 // We expose the API at '/api', '/' (controller-only), and at '/ModelUUID/api' 297 // for the correct location, but other paths should fail. 298 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 299 _, srv := newServer(c, s.State) 300 defer assertStop(c, srv) 301 302 // We have to use 'localhost' because that is what the TLS cert says. 303 addr := fmt.Sprintf("localhost:%d", srv.Addr().Port) 304 305 // '/api' should be fine 306 conn, err := dialWebsocket(c, addr, "/api", 0) 307 c.Assert(err, jc.ErrorIsNil) 308 conn.Close() 309 310 // '/`' should be fine 311 conn, err = dialWebsocket(c, addr, "/", 0) 312 c.Assert(err, jc.ErrorIsNil) 313 conn.Close() 314 315 // '/model/MODELUUID/api' should be fine 316 conn, err = dialWebsocket(c, addr, "/model/dead-beef-123456/api", 0) 317 c.Assert(err, jc.ErrorIsNil) 318 conn.Close() 319 320 // '/randompath' is not ok 321 conn, err = dialWebsocket(c, addr, "/randompath", 0) 322 // Unfortunately go.net/websocket just returns Bad Status, it doesn't 323 // give us any information (whether this was a 404 Not Found, Internal 324 // Server Error, 200 OK, etc.) 325 c.Assert(err, gc.ErrorMatches, `websocket.Dial wss://localhost:\d+/randompath: bad status`) 326 c.Assert(conn, gc.IsNil) 327 } 328 329 func (s *serverSuite) TestNoBakeryWhenNoIdentityURL(c *gc.C) { 330 _, srv := newServer(c, s.State) 331 defer assertStop(c, srv) 332 // By default, when there is no identity location, no 333 // bakery service or macaroon is created. 334 _, err := apiserver.ServerMacaroon(srv) 335 c.Assert(err, gc.ErrorMatches, "macaroon authentication is not configured") 336 _, err = apiserver.ServerBakeryService(srv) 337 c.Assert(err, gc.ErrorMatches, "macaroon authentication is not configured") 338 } 339 340 type macaroonServerSuite struct { 341 jujutesting.JujuConnSuite 342 discharger *bakerytest.Discharger 343 } 344 345 var _ = gc.Suite(&macaroonServerSuite{}) 346 347 func (s *macaroonServerSuite) SetUpTest(c *gc.C) { 348 s.discharger = bakerytest.NewDischarger(nil, noCheck) 349 s.ControllerConfigAttrs = map[string]interface{}{ 350 controller.IdentityURL: s.discharger.Location(), 351 } 352 s.JujuConnSuite.SetUpTest(c) 353 } 354 355 func (s *macaroonServerSuite) TearDownTest(c *gc.C) { 356 s.discharger.Close() 357 s.JujuConnSuite.TearDownTest(c) 358 } 359 360 func (s *macaroonServerSuite) TestServerBakery(c *gc.C) { 361 _, srv := newServer(c, s.State) 362 defer assertStop(c, srv) 363 m, err := apiserver.ServerMacaroon(srv) 364 c.Assert(err, gc.IsNil) 365 bsvc, err := apiserver.ServerBakeryService(srv) 366 c.Assert(err, gc.IsNil) 367 368 // Check that we can add a third party caveat addressed to the 369 // discharger, which indirectly ensures that the discharger's public 370 // key has been added to the bakery service's locator. 371 m = m.Clone() 372 err = bsvc.AddCaveat(m, checkers.Caveat{ 373 Location: s.discharger.Location(), 374 Condition: "true", 375 }) 376 c.Assert(err, jc.ErrorIsNil) 377 378 // Check that we can discharge the macaroon and check it with 379 // the service. 380 client := httpbakery.NewClient() 381 ms, err := client.DischargeAll(m) 382 c.Assert(err, jc.ErrorIsNil) 383 384 err = bsvc.(*bakery.Service).Check(ms, checkers.New()) 385 c.Assert(err, gc.IsNil) 386 } 387 388 type macaroonServerWrongPublicKeySuite struct { 389 jujutesting.JujuConnSuite 390 discharger *bakerytest.Discharger 391 } 392 393 var _ = gc.Suite(&macaroonServerWrongPublicKeySuite{}) 394 395 func (s *macaroonServerWrongPublicKeySuite) SetUpTest(c *gc.C) { 396 s.discharger = bakerytest.NewDischarger(nil, noCheck) 397 wrongKey, err := bakery.GenerateKey() 398 c.Assert(err, gc.IsNil) 399 s.ControllerConfigAttrs = map[string]interface{}{ 400 controller.IdentityURL: s.discharger.Location(), 401 controller.IdentityPublicKey: wrongKey.Public.String(), 402 } 403 s.JujuConnSuite.SetUpTest(c) 404 } 405 406 func (s *macaroonServerWrongPublicKeySuite) TearDownTest(c *gc.C) { 407 s.discharger.Close() 408 s.JujuConnSuite.TearDownTest(c) 409 } 410 411 func (s *macaroonServerWrongPublicKeySuite) TestDischargeFailsWithWrongPublicKey(c *gc.C) { 412 _, srv := newServer(c, s.State) 413 defer assertStop(c, srv) 414 m, err := apiserver.ServerMacaroon(srv) 415 c.Assert(err, gc.IsNil) 416 m = m.Clone() 417 bsvc, err := apiserver.ServerBakeryService(srv) 418 c.Assert(err, gc.IsNil) 419 err = bsvc.AddCaveat(m, checkers.Caveat{ 420 Location: s.discharger.Location(), 421 Condition: "true", 422 }) 423 c.Assert(err, gc.IsNil) 424 client := httpbakery.NewClient() 425 426 _, err = client.DischargeAll(m) 427 c.Assert(err, gc.ErrorMatches, `cannot get discharge from ".*": third party refused discharge: cannot discharge: discharger cannot decode caveat id: public key mismatch`) 428 } 429 430 func noCheck(req *http.Request, cond, arg string) ([]checkers.Caveat, error) { 431 return nil, nil 432 } 433 434 type fakeResource struct { 435 stopped bool 436 } 437 438 func (r *fakeResource) Stop() error { 439 r.stopped = true 440 return nil 441 } 442 443 func (s *serverSuite) bootstrapHasPermissionTest(c *gc.C) (*state.User, names.ControllerTag) { 444 u, err := s.State.AddUser("foobar", "Foo Bar", "password", "read") 445 c.Assert(err, jc.ErrorIsNil) 446 user := u.UserTag() 447 448 ctag, err := names.ParseControllerTag("controller-" + s.State.ControllerUUID()) 449 c.Assert(err, jc.ErrorIsNil) 450 cu, err := s.State.UserAccess(user, ctag) 451 c.Assert(err, jc.ErrorIsNil) 452 c.Assert(cu.Access, gc.Equals, permission.LoginAccess) 453 return u, ctag 454 } 455 456 func (s *serverSuite) TestAPIHandlerHasPermissionLogin(c *gc.C) { 457 u, ctag := s.bootstrapHasPermissionTest(c) 458 459 handler, _ := apiserver.TestingAPIHandlerWithEntity(c, s.State, s.State, u) 460 defer handler.Kill() 461 462 apiserver.AssertHasPermission(c, handler, permission.LoginAccess, ctag, true) 463 apiserver.AssertHasPermission(c, handler, permission.AddModelAccess, ctag, false) 464 apiserver.AssertHasPermission(c, handler, permission.SuperuserAccess, ctag, false) 465 } 466 467 func (s *serverSuite) TestAPIHandlerHasPermissionAdmodel(c *gc.C) { 468 u, ctag := s.bootstrapHasPermissionTest(c) 469 user := u.UserTag() 470 471 handler, _ := apiserver.TestingAPIHandlerWithEntity(c, s.State, s.State, u) 472 defer handler.Kill() 473 474 ua, err := s.State.SetUserAccess(user, ctag, permission.AddModelAccess) 475 c.Assert(err, jc.ErrorIsNil) 476 c.Assert(ua.Access, gc.Equals, permission.AddModelAccess) 477 478 apiserver.AssertHasPermission(c, handler, permission.LoginAccess, ctag, true) 479 apiserver.AssertHasPermission(c, handler, permission.AddModelAccess, ctag, true) 480 apiserver.AssertHasPermission(c, handler, permission.SuperuserAccess, ctag, false) 481 } 482 483 func (s *serverSuite) TestAPIHandlerHasPermissionSuperUser(c *gc.C) { 484 u, ctag := s.bootstrapHasPermissionTest(c) 485 user := u.UserTag() 486 487 handler, _ := apiserver.TestingAPIHandlerWithEntity(c, s.State, s.State, u) 488 defer handler.Kill() 489 490 ua, err := s.State.SetUserAccess(user, ctag, permission.SuperuserAccess) 491 c.Assert(err, jc.ErrorIsNil) 492 c.Assert(ua.Access, gc.Equals, permission.SuperuserAccess) 493 494 apiserver.AssertHasPermission(c, handler, permission.LoginAccess, ctag, true) 495 apiserver.AssertHasPermission(c, handler, permission.AddModelAccess, ctag, true) 496 apiserver.AssertHasPermission(c, handler, permission.SuperuserAccess, ctag, true) 497 } 498 499 func (s *serverSuite) TestAPIHandlerTeardownInitialEnviron(c *gc.C) { 500 s.checkAPIHandlerTeardown(c, s.State, s.State) 501 } 502 503 func (s *serverSuite) TestAPIHandlerTeardownOtherEnviron(c *gc.C) { 504 otherState := s.Factory.MakeModel(c, nil) 505 defer otherState.Close() 506 s.checkAPIHandlerTeardown(c, s.State, otherState) 507 } 508 509 func (s *serverSuite) TestAPIHandlerConnectedModel(c *gc.C) { 510 otherState := s.Factory.MakeModel(c, nil) 511 defer otherState.Close() 512 handler, _ := apiserver.TestingAPIHandler(c, s.State, otherState) 513 c.Check(handler.ConnectedModel(), gc.Equals, otherState.ModelUUID()) 514 } 515 516 func (s *serverSuite) TestClosesStateFromPool(c *gc.C) { 517 pool := state.NewStatePool(s.State) 518 cfg := defaultServerConfig(c) 519 cfg.StatePool = pool 520 _, server := newServerWithConfig(c, s.State, cfg) 521 defer assertStop(c, server) 522 523 w := s.State.WatchModels() 524 defer workertest.CleanKill(c, w) 525 // Initial change. 526 assertChange(c, w) 527 528 otherState := s.Factory.MakeModel(c, nil) 529 defer otherState.Close() 530 531 s.State.StartSync() 532 // This ensures that the model exists for more than one of the 533 // time slices that the watcher uses for coalescing 534 // events. Without it the model appears and disappears quickly 535 // enough that it never generates a change from WatchModels. 536 // Many Bothans died to bring us this information. 537 assertChange(c, w) 538 539 model, err := otherState.Model() 540 c.Assert(err, jc.ErrorIsNil) 541 542 // Ensure the model's in the pool but not referenced. 543 st, err := pool.Get(otherState.ModelUUID()) 544 c.Assert(err, jc.ErrorIsNil) 545 err = pool.Release(otherState.ModelUUID()) 546 c.Assert(err, jc.ErrorIsNil) 547 548 // Make a request for the model API to check it releases 549 // state back into the pool once the connection is closed. 550 addr := fmt.Sprintf("localhost:%d", server.Addr().Port) 551 conn, err := dialWebsocket(c, addr, fmt.Sprintf("/model/%s/api", st.ModelUUID()), 0) 552 c.Assert(err, jc.ErrorIsNil) 553 conn.Close() 554 555 // When the model goes away the API server should ensure st gets closed. 556 err = model.Destroy() 557 c.Assert(err, jc.ErrorIsNil) 558 559 s.State.StartSync() 560 assertStateBecomesClosed(c, st) 561 } 562 563 func assertChange(c *gc.C, w state.StringsWatcher) { 564 select { 565 case <-w.Changes(): 566 return 567 case <-time.After(coretesting.LongWait): 568 c.Fatalf("no changes on watcher") 569 } 570 } 571 572 func assertStateBecomesClosed(c *gc.C, st *state.State) { 573 // This is gross but I can't see any other way to check for 574 // closedness outside the state package. 575 checkModel := func() { 576 attempt := utils.AttemptStrategy{ 577 Total: coretesting.LongWait, 578 Delay: coretesting.ShortWait, 579 } 580 for a := attempt.Start(); a.Next(); { 581 // This will panic once the state is closed. 582 _, _ = st.Model() 583 } 584 // If we got here then st is still open. 585 st.Close() 586 } 587 c.Assert(checkModel, gc.PanicMatches, "Session already closed") 588 } 589 590 func (s *serverSuite) checkAPIHandlerTeardown(c *gc.C, srvSt, st *state.State) { 591 handler, resources := apiserver.TestingAPIHandler(c, srvSt, st) 592 resource := new(fakeResource) 593 resources.Register(resource) 594 595 c.Assert(resource.stopped, jc.IsFalse) 596 handler.Kill() 597 c.Assert(resource.stopped, jc.IsTrue) 598 } 599 600 // defaultServerConfig returns the default configuration for starting a test server. 601 func defaultServerConfig(c *gc.C) apiserver.ServerConfig { 602 return apiserver.ServerConfig{ 603 Clock: clock.WallClock, 604 Cert: coretesting.ServerCert, 605 Key: coretesting.ServerKey, 606 Tag: names.NewMachineTag("0"), 607 LogDir: c.MkDir(), 608 NewObserver: func() observer.Observer { return &fakeobserver.Instance{} }, 609 AutocertURL: "https://0.1.2.3/no-autocert-here", 610 } 611 } 612 613 // newServer returns a new running API server using the given state. 614 // The pool may be nil, in which case a pool using the given state 615 // will be used. 616 // 617 // It returns information suitable for connecting to the state 618 // without any authentication information or model tag, and the server 619 // that's been started. 620 func newServer(c *gc.C, st *state.State) (*api.Info, *apiserver.Server) { 621 return newServerWithConfig(c, st, defaultServerConfig(c)) 622 } 623 624 // newServerWithConfig is like newServer except that the entire 625 // server configuration may be specified (see defaultServerConfig 626 // for a suitable starting point). 627 func newServerWithConfig(c *gc.C, st *state.State, cfg apiserver.ServerConfig) (*api.Info, *apiserver.Server) { 628 // Note that we can't listen on localhost here because TestAPIServerCanListenOnBothIPv4AndIPv6 assumes 629 // that we listen on IPv6 too, and listening on localhost does not do that. 630 listener, err := net.Listen("tcp", ":0") 631 c.Assert(err, jc.ErrorIsNil) 632 srv, err := apiserver.NewServer(st, listener, cfg) 633 c.Assert(err, jc.ErrorIsNil) 634 return &api.Info{ 635 Addrs: []string{fmt.Sprintf("localhost:%d", srv.Addr().Port)}, 636 CACert: coretesting.CACert, 637 }, srv 638 } 639 640 type stopper interface { 641 Stop() error 642 } 643 644 func assertStop(c *gc.C, stopper stopper) { 645 c.Assert(stopper.Stop(), gc.IsNil) 646 }