Router integration with poolmgr and controller

Router uses poolmgr to specialize pods when necessary.

Router uses controller to get the list of triggers to listen for.  For
now this integration is pretty crappy -- we just poll the controller
every few seconds and cache the result.  The right way would be to
have some sort of watch API on the controller and use that.  Or maybe
share access to etcd directly.
This commit is contained in:
Soam Vasani
2016-11-01 18:22:04 -07:00
parent 2b15b726d4
commit 3d5e52dc3d
5 changed files with 79 additions and 28 deletions
+20 -7
View File
@@ -17,29 +17,40 @@ limitations under the License.
package router package router
import ( import (
"errors" "fmt"
"log" "log"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"github.com/platform9/fission" "github.com/platform9/fission"
poolmgrClient "github.com/platform9/fission/poolmgr/client"
) )
type functionHandler struct { type functionHandler struct {
fmap *functionServiceMap fmap *functionServiceMap
poolManagerUrl string poolmgr *poolmgrClient.Client
Function fission.Metadata Function fission.Metadata
} }
func (*functionHandler) getServiceForFunction() (*url.URL, error) { func (fh *functionHandler) getServiceForFunction() (*url.URL, error) {
return nil, errors.New("not implemented") // call poolmgr, get a url for a function
svcName, err := fh.poolmgr.GetServiceForFunction(&fh.Function)
if err != nil {
return nil, err
}
svcUrl, err := url.Parse(fmt.Sprintf("http://%v", svcName))
if err != nil {
return nil, err
}
return svcUrl, nil
} }
func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) { func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *http.Request) {
serviceUrl, err := fh.fmap.lookup(&fh.Function) serviceUrl, err := fh.fmap.lookup(&fh.Function)
if err != nil { if err != nil {
// Cache miss: request the Pool Manager to make a new service. // Cache miss: request the Pool Manager to make a new service.
log.Printf("Not cached, getting new service for %v", fh.Function)
serviceUrl, poolErr := fh.getServiceForFunction() serviceUrl, poolErr := fh.getServiceForFunction()
if poolErr != nil { if poolErr != nil {
log.Printf("Failed to get service for function (%v,%v): %v", log.Printf("Failed to get service for function (%v,%v): %v",
@@ -55,9 +66,11 @@ func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *
} }
// Proxy off our request to the serviceUrl, and send the response back. // Proxy off our request to the serviceUrl, and send the response back.
// TODO: As an optimization we may want to cache proxies too -- this would get us // TODO: As an optimization we may want to cache proxies too -- this might get us
// connection reuse and possibly better performance // connection reuse and possibly better performance
director := func(req *http.Request) { director := func(req *http.Request) {
log.Printf("Proxying request for %v", req.URL)
// send this request to serviceurl // send this request to serviceurl
req.URL.Scheme = serviceUrl.Scheme req.URL.Scheme = serviceUrl.Scheme
req.URL.Host = serviceUrl.Host req.URL.Host = serviceUrl.Host
+46 -17
View File
@@ -17,47 +17,76 @@ limitations under the License.
package router package router
import ( import (
"log"
"time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/platform9/fission" "github.com/platform9/fission"
controllerClient "github.com/platform9/fission/controller/client"
poolmgrClient "github.com/platform9/fission/poolmgr/client"
) )
type HTTPTriggerSet struct { type HTTPTriggerSet struct {
*functionServiceMap *functionServiceMap
*mutableRouter *mutableRouter
controllerUrl string controller *controllerClient.Client
poolManagerUrl string poolmgr *poolmgrClient.Client
triggers []fission.HTTPTrigger triggers []fission.HTTPTrigger
} }
func makeHTTPTriggerSet(fmap *functionServiceMap, controllerUrl string, poolManagerUrl string) *HTTPTriggerSet { func makeHTTPTriggerSet(fmap *functionServiceMap, controller *controllerClient.Client, poolmgr *poolmgrClient.Client) *HTTPTriggerSet {
triggers := make([]fission.HTTPTrigger, 1) triggers := make([]fission.HTTPTrigger, 1)
return &HTTPTriggerSet{ return &HTTPTriggerSet{
functionServiceMap: fmap, functionServiceMap: fmap,
triggers: triggers, triggers: triggers,
controllerUrl: controllerUrl, controller: controller,
poolManagerUrl: poolManagerUrl, poolmgr: poolmgr,
} }
} }
func (triggers *HTTPTriggerSet) subscribeRouter(mr *mutableRouter) { func (ts *HTTPTriggerSet) subscribeRouter(mr *mutableRouter) {
triggers.mutableRouter = mr ts.mutableRouter = mr
mr.updateRouter(triggers.getRouterFromTriggers()) mr.updateRouter(ts.getRouterFromTriggers())
go triggers.watchTriggers() go ts.watchTriggers()
} }
func (triggers *HTTPTriggerSet) getRouterFromTriggers() *mux.Router { func (ts *HTTPTriggerSet) getRouterFromTriggers() *mux.Router {
muxRouter := mux.NewRouter() muxRouter := mux.NewRouter()
for _, trigger := range triggers.triggers { for _, trigger := range ts.triggers {
fh := &functionHandler{ fh := &functionHandler{
fmap: triggers.functionServiceMap, fmap: ts.functionServiceMap,
Function: trigger.Function, Function: trigger.Function,
poolManagerUrl: triggers.poolManagerUrl, poolmgr: ts.poolmgr,
} }
muxRouter.HandleFunc(trigger.UrlPattern, fh.handler) muxRouter.HandleFunc(trigger.UrlPattern, fh.handler)
} }
return muxRouter return muxRouter
} }
func (triggers *HTTPTriggerSet) watchTriggers() { func (ts *HTTPTriggerSet) watchTriggers() {
// watch controller for updates to triggers and update the router accordingly if ts.controller == nil {
return
}
failureCount := 0
maxFailures := 5
// Watch controller for updates to triggers and update the router accordingly.
// TODO change this to use a watch API; or maybe even watch etcd directly.
for {
triggers, err := ts.controller.HTTPTriggerList()
if err != nil {
failureCount += 1
if failureCount >= maxFailures {
log.Fatalf("Failed to connect to controller after %v retries: %v", failureCount, err)
}
}
log.Printf("Updating router, %v triggers", len(triggers))
ts.triggers = triggers
ts.mutableRouter.updateRouter(ts.getRouterFromTriggers())
time.Sleep(3 * time.Second)
}
} }
+1 -1
View File
@@ -49,6 +49,6 @@ func (mr *mutableRouter) ServeHTTP(responseWriter http.ResponseWriter, request *
} }
func (mr *mutableRouter) updateRouter(newHandler *mux.Router) { func (mr *mutableRouter) updateRouter(newHandler *mux.Router) {
log.Print("Updating router") log.Println("Updating router")
mr.router.Store(newHandler) mr.router.Store(newHandler)
} }
+11 -2
View File
@@ -41,8 +41,13 @@ package router
import ( import (
"fmt" "fmt"
"github.com/gorilla/mux"
"net/http" "net/http"
"github.com/gorilla/mux"
controllerClient "github.com/platform9/fission/controller/client"
poolmgrClient "github.com/platform9/fission/poolmgr/client"
"log"
) )
// request url ---[mux]---> Function(name,uid) ----[fmap]----> k8s service url // request url ---[mux]---> Function(name,uid) ----[fmap]----> k8s service url
@@ -64,6 +69,10 @@ func serve(port int, httpTriggerSet *HTTPTriggerSet) {
func Start(port int, controllerUrl string, poolmgrUrl string) { func Start(port int, controllerUrl string, poolmgrUrl string) {
fmap := makeFunctionServiceMap() fmap := makeFunctionServiceMap()
triggers := makeHTTPTriggerSet(fmap, controllerUrl, poolmgrUrl) controller := controllerClient.MakeClient(controllerUrl)
poolmgr := poolmgrClient.MakeClient(poolmgrUrl)
triggers := makeHTTPTriggerSet(fmap, controller, poolmgr)
log.Printf("Starting router at port %v\n", port)
serve(port, triggers) serve(port, triggers)
} }
+1 -1
View File
@@ -33,7 +33,7 @@ func TestRouter(t *testing.T) {
fmap.assign(fn, testServiceUrl) fmap.assign(fn, testServiceUrl)
triggers := makeHTTPTriggerSet(fmap, "", "") triggers := makeHTTPTriggerSet(fmap, nil, nil)
triggerUrl := "/foo" triggerUrl := "/foo"
triggers.triggers = append(triggers.triggers, fission.HTTPTrigger{UrlPattern: triggerUrl, Function: *fn}) triggers.triggers = append(triggers.triggers, fission.HTTPTrigger{UrlPattern: triggerUrl, Function: *fn})