GenericPool is a pool of generic containers for an environment. GenericPoolManager keeps track of all GenericPools, creating them on-demand. The pool manager API is simply a "lookup" for the service URL of a function. If one exists it is returned immediately; otherwise, a generic pool is created, and then a pod is specialized from that pool. poolmgr is designed to run from within the cluster, since it connects to pod IP addresses directly. This is a first cut with many pieces missing. TODO: * Use versioned kubernetes clients instead of the unversioned one * Unit tests for GenericPoolMgr; improve unit test for GenericPool; test for API. * Handle cases where a service exists but pod backing it has failed. * On start up, use existing deployments/pods/services if they exist; in other words don't orphan resources on restart. * Kill idle resources (services, pods, even generic pools) * Autoscale generic pool (for example, by watching num ready pods)
139 lines
3.1 KiB
Go
139 lines
3.1 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 poolmgr
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/platform9/fission"
|
|
"github.com/platform9/fission/cache"
|
|
controllerclient "github.com/platform9/fission/controller/client"
|
|
"io/ioutil"
|
|
"log"
|
|
)
|
|
|
|
type funcSvc struct {
|
|
function *fission.Metadata // function this thing is for
|
|
environment *fission.Environment // env it was obtained from
|
|
serviceName string // name of k8s svc
|
|
|
|
ctime time.Time
|
|
atime time.Time
|
|
}
|
|
|
|
type API struct {
|
|
poolMgr *GenericPoolManager
|
|
functionEnv *cache.Cache // map[fission.Metadata]fission.Environment
|
|
functionService *cache.Cache // map[fission.Metadata]funcSvc
|
|
controller *controllerclient.Client
|
|
}
|
|
|
|
func (api *API) lookupApi(w http.ResponseWriter, r *http.Request) {
|
|
body, err := ioutil.ReadAll(r.Body)
|
|
if err != nil {
|
|
http.Error(w, "Failed to read request", 500)
|
|
return
|
|
}
|
|
|
|
// get function metadata
|
|
m := fission.Metadata{}
|
|
err = json.Unmarshal(body, &m)
|
|
if err != nil {
|
|
http.Error(w, "Failed to parse request", 400)
|
|
return
|
|
}
|
|
|
|
serviceUrl, err := api.lookup(&m)
|
|
if err != nil {
|
|
code, msg := fission.GetHTTPError(err)
|
|
log.Printf("Error: %v: %v", code, msg)
|
|
http.Error(w, msg, code)
|
|
}
|
|
|
|
// return serviceUrl
|
|
w.Write([]byte(serviceUrl))
|
|
}
|
|
|
|
func (api *API) getFunctionEnv(m *fission.Metadata) (*fission.Environment, error) {
|
|
var env *fission.Environment
|
|
|
|
// Cached ?
|
|
result, err := api.functionEnv.Get(m)
|
|
if err == nil {
|
|
env = result.(*fission.Environment)
|
|
return env, nil
|
|
}
|
|
|
|
// Cache miss -- get func from controller
|
|
f, err := api.controller.FunctionGet(m)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Get env from metadata
|
|
env, err = api.controller.EnvironmentGet(&f.Environment)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// cache for future
|
|
api.functionEnv.Set(m, env)
|
|
|
|
return env, nil
|
|
}
|
|
|
|
func (api *API) lookup(m *fission.Metadata) (string, error) {
|
|
// Check function -> svc map
|
|
result, err := api.functionService.Get(m)
|
|
if err == nil {
|
|
// Ok: return svc name
|
|
svc := result.(*funcSvc)
|
|
return svc.serviceName, nil
|
|
}
|
|
|
|
// None exists, so create a new funcSvc:
|
|
|
|
// from Func -> get Env
|
|
env, err := api.getFunctionEnv(m)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// from Env -> get GenericPool
|
|
pool, err := api.poolMgr.GetPool(env)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// from GenericPool -> get one function container
|
|
funcSvc, err := pool.GetFuncSvc(m)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// add to cache
|
|
err = api.functionService.Set(m, funcSvc)
|
|
if err != nil {
|
|
// log and ignore error
|
|
log.Printf("Error saving function service: %v", err)
|
|
}
|
|
|
|
return funcSvc.serviceName, nil
|
|
}
|