Pool Manager -- manage generic containers and their specialization
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)
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user