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  }