github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/testing/fakeapi.go (about) 1 package testing 2 3 import ( 4 "crypto/tls" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "reflect" 9 10 "github.com/bmizerany/pat" 11 "github.com/juju/utils" 12 "golang.org/x/net/websocket" 13 14 "github.com/juju/juju/apiserver/observer/fakeobserver" 15 "github.com/juju/juju/rpc" 16 "github.com/juju/juju/rpc/jsoncodec" 17 "github.com/juju/juju/rpc/rpcreflect" 18 "github.com/juju/juju/testing" 19 ) 20 21 // Server represents a fake API server. It must be closed 22 // after use. 23 type Server struct { 24 // Addrs holds the address used for the 25 // server, suitable for including in api.Info.Addrs 26 Addrs []string 27 28 *httptest.Server 29 newRoot func(modelUUID string) interface{} 30 } 31 32 // NewAPIServer serves RPC methods on a localhost HTTP server. 33 // When a connection is made to the API, the newRoot function 34 // is called with the requested model UUID and the returned 35 // value defines the API (see the juju/rpc package). 36 // 37 // Note that the root value accepts any facade version number - it 38 // is not currently possible to use this to serve several different 39 // facade versions. 40 // 41 // The server uses testing.ServerCert and testing.ServerKey 42 // to host the server. 43 // 44 // The returned server must be closed after use. 45 func NewAPIServer(newRoot func(modelUUID string) interface{}) *Server { 46 tlsCert, err := tls.X509KeyPair([]byte(testing.ServerCert), []byte(testing.ServerKey)) 47 if err != nil { 48 panic("bad key pair") 49 } 50 51 srv := &Server{ 52 newRoot: newRoot, 53 } 54 pmux := pat.New() 55 pmux.Get("/model/:modeluuid/api", http.HandlerFunc(srv.serveAPI)) 56 57 srv.Server = httptest.NewUnstartedServer(pmux) 58 59 tlsConfig := utils.SecureTLSConfig() 60 tlsConfig.Certificates = []tls.Certificate{tlsCert} 61 srv.Server.TLS = tlsConfig 62 63 srv.StartTLS() 64 u, _ := url.Parse(srv.URL) 65 srv.Addrs = []string{u.Host} 66 return srv 67 } 68 69 func (srv *Server) serveAPI(w http.ResponseWriter, req *http.Request) { 70 wsServer := websocket.Server{ 71 Handler: func(conn *websocket.Conn) { 72 srv.serveConn(conn, req.URL.Query().Get(":modeluuid")) 73 }, 74 } 75 wsServer.ServeHTTP(w, req) 76 } 77 78 func (srv *Server) serveConn(wsConn *websocket.Conn, modelUUID string) { 79 codec := jsoncodec.NewWebsocket(wsConn) 80 conn := rpc.NewConn(codec, &fakeobserver.Instance{}) 81 82 root := allVersions{ 83 rpcreflect.ValueOf(reflect.ValueOf(srv.newRoot(modelUUID))), 84 } 85 conn.ServeRoot(root, nil) 86 conn.Start() 87 <-conn.Dead() 88 conn.Close() 89 } 90 91 // allVersions serves the same methods as would be served 92 // by rpc.Conn.Serve except that the facade version is ignored. 93 type allVersions struct { 94 rpcreflect.Value 95 } 96 97 func (av allVersions) FindMethod(rootMethodName string, version int, objMethodName string) (rpcreflect.MethodCaller, error) { 98 return av.Value.FindMethod(rootMethodName, 0, objMethodName) 99 }