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.
90 lines
2.7 KiB
Go
90 lines
2.7 KiB
Go
/*
|
|
Copyright 2016 The Fission Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package router
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httputil"
|
|
"net/url"
|
|
|
|
"github.com/platform9/fission"
|
|
poolmgrClient "github.com/platform9/fission/poolmgr/client"
|
|
)
|
|
|
|
type functionHandler struct {
|
|
fmap *functionServiceMap
|
|
poolmgr *poolmgrClient.Client
|
|
Function fission.Metadata
|
|
}
|
|
|
|
func (fh *functionHandler) getServiceForFunction() (*url.URL, error) {
|
|
// 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) {
|
|
serviceUrl, err := fh.fmap.lookup(&fh.Function)
|
|
if err != nil {
|
|
// 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()
|
|
if poolErr != nil {
|
|
log.Printf("Failed to get service for function (%v,%v): %v",
|
|
fh.Function.Name, fh.Function.Uid, poolErr)
|
|
// We might want a specific error code or header for fission
|
|
// failures as opposed to user function bugs.
|
|
http.Error(responseWriter, poolErr.Error(), 500)
|
|
return
|
|
}
|
|
|
|
// add it to the map
|
|
fh.fmap.assign(&fh.Function, serviceUrl)
|
|
}
|
|
|
|
// Proxy off our request to the serviceUrl, and send the response back.
|
|
// TODO: As an optimization we may want to cache proxies too -- this might get us
|
|
// connection reuse and possibly better performance
|
|
director := func(req *http.Request) {
|
|
log.Printf("Proxying request for %v", req.URL)
|
|
|
|
// send this request to serviceurl
|
|
req.URL.Scheme = serviceUrl.Scheme
|
|
req.URL.Host = serviceUrl.Host
|
|
req.URL.Path = serviceUrl.Path
|
|
// leave the query string intact (req.URL.RawQuery)
|
|
|
|
if _, ok := req.Header["User-Agent"]; !ok {
|
|
// explicitly disable User-Agent so it's not set to default value
|
|
req.Header.Set("User-Agent", "")
|
|
}
|
|
}
|
|
proxy := &httputil.ReverseProxy{Director: director}
|
|
proxy.ServeHTTP(responseWriter, request)
|
|
|
|
// TODO: handle failures and possibly retry here.
|
|
}
|